해당 대회는 사정상 참여를 못해서 대회 종료 후 문제를 풀어봤다.


먼저 문제에서 제공해주는 페이지에 들어가보면 간단한 패스워드 입력창만 있고 따로 동작을 안한다. 근데 소스를 보면 주석에 아래와 같은 구문이 있다.


<!-- | Dark Web Message Board | DEVELOPED BY K1tsCr3w | Open source at Kits-AB | -->


해당 구문을 참조해 github에서 Kits-AB를 찾아보면 문제 사이트의 소스가 공개되어 있다.


app.py


import os


from model import init_database, Post

from flask import render_template, Flask, send_from_directory, request



init_database()


app = Flask(__name__, static_folder='static')


@app.route('/robots.txt')

def static_from_root():

    return send_from_directory(app.static_folder, request.path[1:])


@app.route("/")

def index():

    return render_template("login.html")



@app.route("/boards/<id>")

def board(id):

    posts = []


    if int(id) == 1:

        posts = Post.select()

    

    return render_template("board.html", posts=posts)



if __name__ == "__main__":

    if os.environ.get("FLASK_DEBUG"):

        app.run(debug=True)

    else:

        app.run()


메인 코드인데 보면, boards/1 경로에 접근하면 뭔가 데이터를 읽어서 뿌려주는걸 볼 수 있었다. 해당 페이지에 접근해보면 아래와 같이 여러 데이터들이 보이는데 그 중 암호화 된 값 하나가 보이는걸 볼 수 있었다.



해당 문제가 web+crypto+osint 였기 때문에 해당 암호문을 일단 디크립트 해야할 거라는 생각이 들었다. crypto는 공부를 거의 안해봐서 그만볼까 했는데 깃헙 소스에서 디크립트 하는 코드를 아래와 같이 제공해줘서 좀 더 풀어봤다.


test_crypto.py


from cryptography.hazmat.backends import default_backend

from cryptography.hazmat.primitives.asymmetric import rsa, padding

from cryptography.hazmat.primitives import serialization, hashes


import base64


import unittest



# Maybe this could be used to encrypt the secret messages in the board? 

# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/


MESSAGE=b"Something secret"


PEM_PASSWORD=b'aVerySecretPassword'

       

PEM_PRIVATE_KEY=b"""-----BEGIN ENCRYPTED PRIVATE KEY-----

MIIFHTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIeJ8sEumQimECAggA

MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCoq4tCJ4RHgmF8/Ayi+gRMBIIE

wA/ByLKYec9EnYxdklKLK3nnilG17fYrEeXGhkRy0tHuxDDJFrvZXANyiakSnj/r

0Ly52heKxEkXYTQ8ohJR5Fezn8KXLYJVdvkJkAGURiVICPb10f1m7UwqakPSt4Hk

nwXZRXYDyiNyUoMgIdxxpaNvl0h6GotOaa/CvcACnozZxiZv3X7f9+0y7zKYA+i8

lM5qaiFjz06LdQ0+MvSxqpC0lKbEJTrTvd95TsdkwNppoQQXU4p/CiGtrRC3DmCd

YZCSLAm7mlVfpnP2wcN7rX3rPQtlb0LCiWbLw2DmKaAbgW4yiqP12+yX0cegxZPH

KuvBtqDOEODDhro/j/VBSizhZxB9xgpsd1ZVdmIUGHmsckEg0pmHcOmb+L3/UwCX

6WI5HMecRk2miNnjZt19YPAdJJ0CNURnqkRMKw5dhy1e3V2+W1K2ojICJj7gZaSh

Hclt3VbwbjAQNwPUU2kkJWCQFDAjLnEmOgZEzESuo58kt3WyurJbeC5H5irTRlaT

jP9jCOvbuE2P4JR5ErOx5wxbMhI+UEVdcuHYGXoyJKLatg+i8W82BV+RQA9d7Bmq

qKdEWtLCD0IT9eCCm//M6iZiVHuDGjxgZVfvzaU7yHMdZdVi5mKfxHeIcGyrolVu

LDsOrjZ9aHtgVycMGjpltYdhJpTlP3Z2Otby18H0bUv1ntsRBZdx2lle8A1Jre1n

10DH5Lx5rn7prJuj/IL1q/Z4lcDlkvHI6I0m/rauXyddGcUrINTTWq9ujQ8x09Gt

NbLeoMOLy39H55W/T7VO+ds1kEOObE5lYwh0Jo29LpHLlKKpKVx23IHBTjC5LEAV

4qynUw1BLK1klEClZp9AfTAfz5M9AjK50l3MEEwIW48eS3U6h137Of3QirMjiE82

iFANV3rOYdsmQAtDeWxx+N3sLv8kK8ANnr85Dj9QOXQJtAm9S7UZM9BrwIgmOuVL

9r9Pt5J8B0lAwPQ5+sxTfgPrd0FhZSZYzrelbp0ck4odSnXFK+ZL0E1VWIBXUtTd

oj5lFFs9U95vXU5szx17xB+IMd2KOKIirIEwCm3TIa58sMbhLxDJtWpqlFVztg/E

zBeD3dzvhJzitTzKvFYTrzbge+o3/dK2+yFbibE0VTAGV60ILoZq5kLVqYgihk8I

7UHLw7ugunteNLXBpB2QEvETGXhjPu82dqZFS4q+KQkIm6n6XCh1oe/CpLg08Zzh

fAWLBv1OSs/tL9cRUWhY0JxcksP6jZrhNgBzqmN4mIeQ8BfaVQbgEaD/r0c4HgS/

68dRofW02JsfaNy0qgtnsWIvAez/2gq4Sryo3NJMX0V5YogmNAWl4dsonXVE5Yss

mR/0xgLIRqKB2S32ycBjCg0BJNDJE8KSpWZHPTZxel5NQqvOUzfoc7fA2B01OhQJ

EGRgwpp+4kPEU4cZz0FUN7Yv7YRWdkVgd0BJVHVdwog1/mX3hz5SktYoU9mzuuEV

COm52E8EDJmH+eDDmOcFoXDx9rV8vcnf8AMDE1eGRxuF6YjrdsOEhaCBaQXdB+0f

S2eccZTxfvwVCsVUsy2WrWJ6+C1qG7g3vsFiKy72eWjZ1BE5k1KZ/AMxQRi4wraL

jmt95WyzLVitJ54jC6KqXZQ=

-----END ENCRYPTED PRIVATE KEY-----"""


ENCRYPTED_MESSAGE=('KA6I/Hu3sUWtPIvqmWEUHctAtDwWm7ZSg1GhTOwZMOgZhxi+WobWX+Q+J4Mym9zW9CwKZnILBi9tP'

        '+fXkionJC3U4A7APl6MPjtbkSPTqB6BXPug57dOVH2bKoyGCOkb1Y7GGs/wIVCebDyRH8katXP99q80y8Mr7wzw'

        '+xL7dNcn01Ho6xYZQlbakqJOl2UCorFGReOryGgNfhYxnHWmSDkQDtFBsB/RnexqftYLVrnPiStwALsoO8eYLsI'

        '1wnI1kmr5acbAFcW1G/0x4EZ/iouVu0EYisgQ8GXcwoed3wgQhUdrFAmI6DcbElza6QveNXCSsIIwjLWpzI2NrwPjYg==')


DEFAULT_PADDING=padding.OAEP(

    mgf=padding.MGF1(algorithm=hashes.SHA256()),

    algorithm=hashes.SHA256(),

    label=None

)



class TestEncryption(unittest.TestCase):

    

    def test_encryption_decryption(self):

        private_key = rsa.generate_private_key(

            public_exponent=65537,

            key_size=2048,

            backend=default_backend()

        )

        ciphertext = private_key.public_key().encrypt(

            MESSAGE,

            DEFAULT_PADDING

        )

        plaintext = private_key.decrypt(

            ciphertext,

            DEFAULT_PADDING

        )

        self.assertEqual(plaintext, MESSAGE)


    def test_generate_private_key(self):

        private_key = rsa.generate_private_key(

            public_exponent=65537,

            key_size=2048,

            backend=default_backend()

        )


        private_pem = private_key.private_bytes(

            encoding=serialization.Encoding.PEM,

            format=serialization.PrivateFormat.PKCS8,

            encryption_algorithm=serialization.BestAvailableEncryption(PEM_PASSWORD)

        )

        # print(private_pem.decode())

    

    def test_encryption(self):

        private_key=serialization.load_pem_private_key(

            PEM_PRIVATE_KEY,

            password=PEM_PASSWORD,

            backend=default_backend()

        )

        ciphertext = private_key.public_key().encrypt(MESSAGE, DEFAULT_PADDING)

        encoded_cipher = base64.b64encode(ciphertext)

        # print(encoded_cipher)

        # print(encoded_cipher.decode())

    

    def test_decryption(self):

        private_key = serialization.load_pem_private_key(

            PEM_PRIVATE_KEY,

            password=PEM_PASSWORD,

            backend=default_backend()

        )

        plaintext = private_key.decrypt(

            base64.b64decode(ENCRYPTED_MESSAGE.encode("utf-8")),

            DEFAULT_PADDING

        )

        #print(plaintext)

        self.assertEqual(MESSAGE, plaintext)


if __name__ == '__main__':

    unittest.main()


코드에서 private key랑 password가 하드코딩으로 박혀있길래 암호화 값만 바꿔서 돌려봤는데 패스워드가 틀렸는지 정상적으로 복호화가 안됬다.


해볼만한게 딱히 안떠올라서 그냥 dictionary attack으로 사전파일 받아다 브루트 포스 돌렸는데도 복호화가 안됬다.


여기서 뭐지하다 깃헙 소스를 좀더 보다보니 commits이 3번 있었던게 눈에 들어왔다.


3번의 커밋 중 removed the production key, luckily it was encrypted with a password … 라는 커밋이 눈에 들어왔고 확인해보니 private key가 변경되었던 걸 확인할 수 있었다.


변경 전 private key를 통해 아래 코드로 브포를 돌리니까 암호화된 값이 복호화가 되었다.


decrypt.py

 

from cryptography.hazmat.backends import default_backend

from cryptography.hazmat.primitives.asymmetric import rsa, padding

from cryptography.hazmat.primitives import serialization, hashes


import base64


import unittest


PEM_PRIVATE_KEY = b"""-----BEGIN ENCRYPTED PRIVATE KEY-----

MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIF+TK17Q9CAsCAggA

MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCebicNIgfA441g2E3t3z/oBIIE

0OAMyvjZ8MaFDLJzuzDY3RWHP0IHWiHoCBNxPJWySon/tLXoizSbsj8EKtgA0MpE

vORC4QdnKg7bqplAAXfSIRli9Hb7RcuMpKv5buW3/Oh/th8NWWM9LOQOBAO0svlR

pJhA5hZSKEgEJMd1E77mjv29gHMEzRgXvAsTOZXhgbbtPnIkQGPXZq4hXyhy0VBt

9cCevKYLgVFahIARjejN+KErNiSN0f76mc62wunum+J6uGtk/HYZ00ZsFcf/0x7B

O/8hrFsliAg+2izNLVWy/+b1oCkuaMIEZ0zXjse3iZirSmWs6F5tFGh2w5lnJB1G

hJAqTjhHdvPWpwiyTw4nCG7+FDd3v1Ih+v8Qq9evlkYg1rdwh13ymGcfko3y7p2l

SuQsJ94i5NEv4acgIE70fqXrwzbSlc+QB5RtKexMj0NxWCySe9seLQP9fbCxp6Ci

a8mHS/4hF7hBbH984QJxy7aqt+U/xLQrKkkp2Lf0KYfthmiS13e7ZEtNSzd3dxZv

eVnDNSzEh/ty/+yt5bx58AlmhNigkaPX+KrTYt1KgQBrgYyk/YNEWK8GE0Sq/4KL

uEiIa0mpbn9je7szIA9egwjIqLWasBoG1HOb5dOu/azhVoM8mheEik/FQLHhgZlo

ZoFY8Rb3jO3Mv/sod1tQE6IteAkBsfXGT8QNaJHMAjmf96aNA8y0bStpHm1ZzpzW

qX3xcr6bDAt4olonDZ1DNTZh4AnSCnKM8LM6kwwY0r8q13EHJ2Ek6L0Vh+BiIeNw

7Q/jQ1thXzrYv9e5KU5TmvZAvtXoqcUCmI2ehnOq6xmir07g4tPQIHyolbY8EHw1

r/mb3me1+8lPdvjKSCM/LqI04h3GPkfnXWwPwlBL4sd5mnKRunLHcnLDu2AVRE+R

r8DvGGIMNr+LZjxZIdjhMraR6VSSTXX028Lamz40ZY9gn3vQWeIJAi0S7g/TW+TJ

RwXGW5gmLfbzlkzgvXPRPfjk9EeBtcS4Pj7q2QIrrAdZZFCC4z5uRGmMHC/tv2/p

IYpV2kClKcnNuPvQSreJXB18GJo1VJU/o78/Hi/cr1atiERM38gP1FYk08vcwjwT

Av62VWaTXsuAsOzS/fjmSsyAlv0LN8pNJ6j3uvk+bOrbKS4V7aM0oHDhLtlJThN5

dagcklxP1VgRAXQPdGUz1oEZzoKezPxq2mJCj8QAPZFkat5mRzbUum0aAr3Yn7Vq

KLGrILx8p4sToqfiKMnayU/QCpgifgJbMun9pSvdOC40b8xUIeuN0PlIkLueA4Mu

o4pbU2inYbC+vEB3c1fHaki+Z0+jUuHyIWtEBJOD6VNYx1LU3HY6T7eV8t/8oJxi

LZCxhon+/R9kEgJO0ofp0362pFm5i1V1afzjFMAhFK4khFNdZJ6rJLrymg1ueCsx

sxSv8x8EA/ZykDJs4M/E5eSiZI9ZmrCsIrUXZ7QGjguqHXnHi7wsO3RSa2c8Bl+t

+SYlmqK5U55yHZ23rJIS/XNIaMB+mX0CHnx/+rohABcueD7Hz7Q0OHP34NuPwK3x

NAx6x4Yfrw2SiYd0Nj15N8oexI+u6/tahCL2obap9S1Y7zibfNgJs4d2yi3F3A+w

Fe+whD+k+txSfs6w50MFgI4JG2Hu6dLtdQC5FSyOAYDJ

-----END ENCRYPTED PRIVATE KEY-----"""


ENCRYPTED_MESSAGE = ('rW+fOddzrtdP7ufLj9KTQa9W8T9JhEj7a2AITFA4a2UbeEAtV/ocxB/t4ikLCMsThUXXWz+UFnyXzgLgD9RM+2toOvWRiJPBM2ASjobT+bLLi31F2M3jPfqYK1L9NCSMcmpVGs+OZZhzJmTbfHLdUcDzDwdZcjKcGbwEGlL6Z7+CbHD7RvoJk7Ft3wvFZ7PWIUHPneVAsAglOalJQCyWKtkksy9oUdDfCL9yvLDV4H4HoXGfQwUbLJL4Qx4hXHh3fHDoplTqYdkhi/5E4l6HO0Qh/jmkNLuwUyhcZVnFMet1vK07ePAuu7kkMe6iZ8FNtmluFlLnrlQXrE74Z2vHbQ==')


DEFAULT_PADDING = padding.OAEP(

    mgf=padding.MGF1(algorithm=hashes.SHA256()),

    algorithm=hashes.SHA256(),

    label=None

)


def test_decryption(password):

    try:

        private_key = serialization.load_pem_private_key(

            PEM_PRIVATE_KEY,

            password=password,

            backend=default_backend()

        )

        plaintext = private_key.decrypt(

            base64.b64decode(ENCRYPTED_MESSAGE.encode("utf-8")),

            DEFAULT_PADDING

        )

        print("Find Password = " + password)

        print("Find Decrypt Text = " + plaintext)

        exit()

    except:

        tmp = 0


f = open("C:\Users\Administrator\Desktop/rockyou.txt","r")

i = 0

password = f.read()

pass_list = password.split("\n")

print len(pass_list)

for i in xrange(len(pass_list)):

    test_decryption(pass_list[i])


Result
Find Password = falloutboy
Find Decrypt Text = Bank url: http://bankofsweden-01.pwn.beer


복호화된 url에 접근해보니 안되서 문제설명을 다시보니 url을 찾은다음 5000 포트로 접근하라는 문장이 있었다. 

 http://bankofsweden-01.pwn.beer:5000 경로로 들어가보니 아래와 같이 사이트가 하나 나왔다. 




기능 중에 로그인 및 회원가입 기능이 있었는데 그냥 회원가입 후 로그인해보면 계정 활성화가 안되었다고 나왔다. 그래서 계정명으로 사용한 이메일로 활성화 메일이 오나 했는데 아무것도 오는게 없었다. 여기서 취약점을 터트려야하나 싶어서 보니 회원가입 할 때 is_active란 파라미터가 있었다. 해당 파라미터를 true로 세팅하고 회원가입해주면 로그인이 되었다.


로그인 후 사이트를 쭉 보다보면 export하는 기능을 사용할 때 아래와 같이 lfi가 터지는걸 볼 수 있었다.



대충 lfi로 아래 순서로 파일을 쭉쭉 leak하다보면 아래와 같이 app.py 파일에 플래그가 존재하는 걸 볼 수 있었다.


/proc/self/environ

/proc/self/cmdline

/home/bos/ctf/app.py










'CTF > Writeup' 카테고리의 다른 글

ISITDTU CTF 2019 Web Write up  (0) 2019.07.01
Google CTF 2019 Quals bnv  (0) 2019.06.24
DEF CON CTF Qualifier 2019 cant_even_unplug_it  (0) 2019.05.13
angstrom ctf 2019 Web Write up  (0) 2019.04.25
ASIS CTF 2019 Fort Knox  (0) 2019.04.22
블로그 이미지

JeonYoungSin

메모 기록용 공간

,