계산기

 

버튼 투르면 /calc.php?num= 로 ajax request 날린다

 

eval($_GET[num])으로 서버단 코드를 예상해볼 수 있다

 

근데 필터링이 좀 있는데 0x4 같은 16진수 표현도 안되는거 보니 '/[0-9]/'로 필터링 걸려있는 것 같다.

 

 

역시 또 한참 헤매다 라업 봤는데

 

와.. 이건 진짜 몰랐다.

 

 

?num=0x1   -> filtered

?%20num=0x1  -> ok  - num=1

?%20num[123=0x1  -> ok - num_123=1

?%20num[123%00456=0x1  -> ok -num_123=1

 

 

waf와 php에서 uri를 파싱하여 파라미터를 가져오는 방식의 차이에서 발생할수도 있는 문제라고 한다.

 

뭐.. 쨌든 이렇게 구하면 var_dump(scandir('/')) 이런거 하면 되는데

 

'," 이런것도 필터링이라 그냥 var_dump(scandir(chr(0x2f))) 이런거 해보면 될거같다

 

ㅇㅋㅇㅋ

 

var_dump(file_get_contents(var_dump(file_get_contents(chr(0x2f).chr(0x66).chr(0x31).chr(0x61).chr(0x67).chr(0x67)))

 

음.. 또 개발 중국어 번역..

 

대충 www.tar.gz를 받아야 할거 같다

 

받아보면 3003개의 php파일이 있다.

 

이런 난독화?된 php 소스들이 3003개인건데, system, eval 과 같이 웹쉘로 이용될 수 있는 함수들이 보인다.

 

한마디로 문제의 논지는 해커가 웹서버를 해킹하여 웹쉘을 올려놨는데 3003개중에서 웹쉘로 이용될 수 있는 아이를 찾아 실행시켜야 하는것이다.

 

 

솔직히 말하자면 이문제에 삽질 너무 했다..(많이는 아니지만)

 

코드를 보면 함수도 선언되어있고 system에 $_GET으로 user input을 받는데 3항 연산자로 선언하여 결국엔 user input이 system과 같은 함수에 도달하지 못하는 경우도 있고.. 주석 안에 코드가 있는 경우도 있고 뭐 이런 여러가지 난독화 기법이 걸려있었다.

 

나는 이를 optimization해야 하는 문제라 생각하여 주석을 지우고 쓸데없는 함수(선언되지 않고 정의만 된 함수)를 삭제하고 뭐 이런걸 했는데 optimizing을 어느정도 다 했는데 시스템 명령을 실행할 수 있는 함수가 없는것이다 (..!)

 

사실 optimizing을 하면서도 뭔가 '아 이렇게 고생할거면 그냥 get,post user input만 파싱해와서 쭉 브포때리면 되지 않을까' 생각은 했었는데 optimizing 자체에 목표가 생겨버려서(?) 하다보니 해답과는 멀어진것 같다.

 

쨌든

 

optimization을 하는 코드만 첨부하고 이번 문제는 마무리하겠당.

 

뭐 이 코드에서 get,post 파싱하는 부분도 있으니 이거 가져와서 쭉쭉 짜면 될거같은데 귀찮아서 그냥 라업보고 풀었다

 

#-*-coding: utf-8-*-
import requests
import sys
import urllib
import time
import sys
import struct
import os
import locale
import re
locale.getpreferredencoding()
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

with open('eval_var.txt','rb') as f:
    eval_vars = f.read().split('\n')

fun_re = re.compile('function \w{1,15}\(.+\)\{.+\}',re.DOTALL)

cnt = 0
for x in os.walk('src/'):
    x[2].sort()
    for files in x[2]:
        #print '[+] doing this.. {}'.format(files)
        with open('./src/'+files,'rb') as f:
            lines = f.read()
        tr = ''
        
        # removing comment
        if '/*' in lines:
            tr += lines.split('/*')[0]
            #print lines.split('/*')
            for i in lines.split('/*')[1:]:
                try:
                    tr += i.split('*/')[1]
                except:
                    print 1-2
        lines = tr
        tr = ''

        # removing unuse functions
        p = re.compile('function (\w|\d){1,20}\(\)')
        if p.findall(lines):
            tr += lines.split('function ')[0]
            for i in lines.split('function ')[1:]:
                fn = i.split('()')[0]
                if len(re.compile(fn).findall(lines)) > 1:
                    tr += 'function {}\n'.format('}'.join(i.split('}\n    \n')))
                else:
                    tr += '}'.join(i.split('\n    \n}')[1:])
                        

        lines = tr

        # removing unuse $_GET / $_POST

        p = re.compile("\$_((GET)|(POST))\[\\'.*\\'\]")
        tr = ''
        if p.findall(lines):
            t = lines.split('\n')
            for i in t:
                if p.findall(i):
                    if re.compile("\$_((GET)|(POST))\[.*\] \?\? ' '").findall(i):
                        pass
                    elif re.compile("\$_((GET)|(POST))\[.*\] = ' ';").findall(i):
                        pass
                    elif re.compile("\$_((GET)|(POST))\[\\'.*\\'\] \?\? \\' \\'").findall(i):
                        pass
                    else:
                        if re.compile("echo `{\$_GET\['.*'\]}`;").findall(i):
                            varname = re.compile("echo `{\$_GET\['").split(i)[1].split("'")[0]
                            if len(re.compile(varname).findall(lines)) == 1:
                                print 'high availability - {} : {}'.format(files, i)
                                print len(re.compile(varname).findall(tr))
                                exit(1)
                            else:
                                for jj in tr.split('\n'):
                                    if varname in jj:
                                        print files
                                        print jj
                                        print '--------------\n'
                        else:
                            print'-----------------'
                            print i
                            print'-----------------'
                            print lines
                            exit(1)
                           
                            
                        tr += i+'\n'
                else:
                    tr += i+'\n'

        lines = tr

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Web > BUUCTF' 카테고리의 다른 글

[BUUCTF] - [SUCTF 2019]CheckIn1  (1) 2020.02.15
[BUUCTF] - [RoarCTF 2019]Easy Calc1  (0) 2020.02.14
[BUUCTF] - [强网杯 2019]高明的黑客1  (0) 2020.02.14
[BUUCTF] - [HCTF 2018]admin1  (0) 2020.02.13
[BUUCTF] - [SUCTF 2019]EasySQL1  (0) 2020.02.13
[BUUCTF] - [护网杯 2018]easy_tornado1  (0) 2020.02.13

/change에 보면 주석으로 github 주소가 있다.

 

auditing gogo

 

 

register 함수

 

userinput인 username을 strlower()로 던져 그 결과를 가지고 if User.query.filter_by(username=name).first() 구문으로 중복 회원 검사를 한다.

 

 

비밀번호를 바꿀 수 있는 change() 함수.

 

여기도 마찬가지로 strlower로 name을 소문자로 만드는데, session에 박힌 name을 이용한다.

 

 

 

login() 함수

 

마찬가지로 strlower로 소문자화 후 이를 name에 저장한다.

 

 

 

정리하자면

 

register -> strlower(input_name) -> db_name

login -> strlower(input_name) -> session_name

change -> strlower(session_name) -> db_name(update)

 

오잉 change에서 이미 strlower가 된 user input name을 한번 더 strlower한다

 

 

그리고 python에선 아시는분들은 아시겠지만 string을 소문자화 하는 메서드는 string.lower()이다.

 

 

해당 소스코드에서 strlower가 커스텀으로 구현되어있는데 뭐 이상한 nodeprep.prepare를 사용한다고 한다.

 

찾아보면 뭐 유니코드 이슈가 있다고하는데 자세한건 첨부하기 귀찮고,

 

대충 파이썬으로 슥슥 코드짜서 nodeprep.prepare 함수에 대한 unicode fault를 전수조사 해보면 0xe1b4ac~0xe1b582가량까지 유니코드를 넣으면 영대문자가 나오는 것을 확인할 수 있다.

 

그럼 저 문자를 사용하여 가입을 하면

 

[register]

(user input) adᴹin -> adMin (db)

 

[login]

(user input) adᴹin -> adMin (session)

 

[change]

(session) adMin -> admin (db)

 

kia~~ 우리는 이제 admin의 비번을 바꿀 수 있다.

 

 

 

 

 

 

 

'Web > BUUCTF' 카테고리의 다른 글

[BUUCTF] - [RoarCTF 2019]Easy Calc1  (0) 2020.02.14
[BUUCTF] - [强网杯 2019]高明的黑客1  (0) 2020.02.14
[BUUCTF] - [HCTF 2018]admin1  (0) 2020.02.13
[BUUCTF] - [SUCTF 2019]EasySQL1  (0) 2020.02.13
[BUUCTF] - [护网杯 2018]easy_tornado1  (0) 2020.02.13
[BUUCTF] - [强网杯 2019]随便注1  (0) 2020.02.12

 

또 multiline sqli.

 

set @a='query';

prepare pstmt1 from @a;

execute pstmt1;

 

 

지난번엔 위에처럼 pstmt 가지고 풀었었는데 이번엔 아쉽게도 from과 '(%27)이 필터링이다.

 

한가지 특이한점은 어떤값을 넣어도 1이 나온다는 것이다.

 

이로 미루어보아

 

select $_GET["q"]||1 from Flag;

 

처럼 쿼리가 구성이 되어있지 않을까 유추해볼 수 있따

 

근데 찾아보니 이건 unintended라고 한다.

 

 

intended

 

예상 쿼리가 {input}||1이 아니라 {input}||flag였다고 하네?

 

1;set sql_mode=PIPES_AS_CONCAT;select 1

 

||를 concat으로 해석하게 하여 플래그를 출력한다는 것이당,,,

 

oracle에선 ||이 기본으로 concat이 되지만 mysql에선 or 와 동일하다. 이 모드를 바꿔주는것,,

 

 

 

 

 

'Web > BUUCTF' 카테고리의 다른 글

[BUUCTF] - [强网杯 2019]高明的黑客1  (0) 2020.02.14
[BUUCTF] - [HCTF 2018]admin1  (0) 2020.02.13
[BUUCTF] - [SUCTF 2019]EasySQL1  (0) 2020.02.13
[BUUCTF] - [护网杯 2018]easy_tornado1  (0) 2020.02.13
[BUUCTF] - [强网杯 2019]随便注1  (0) 2020.02.12
[BUUCTF] - [HCTF 2018]WarmUp1  (2) 2020.02.11

흠, filename을 받고 filehash를 받는다.

hints.txt에는 md5로 해싱한 파일네임을 쿠키 시크릿이랑 또 해싱해서 파일해시를 구한다고 한다.

 

 

틀린 해시를 입력하면 `/error?msg=Error`로 넘어가게 된다.

 

여기서 좀 헤맸는데, msg 파라미터로 들어간 값이 response에 그대로 찍히는걸 보고 ssi나 ssti를 의심했어야 했다.

 

 

앗.. 빙고

 

ssti(server side template injeciton)이다.

 

근데 필터링이 좀 많다.

 

() [] + - * / _ 등 많은 특수문자가 필터링 되고 있다.

 

허,, 여기서도 한참 헤맸는데

 

문제이름이 easy_toranado인걸 착안해서 github에 tornado를 찾아보면

 

 

 

 

 

 

 

tornado라는 이름의 python으로 구현된 web server가 나온다.

 

 

 

여기서 cookie_secret을 찾아보면 

 

 

web.py에 create_signed_value()에 self.application.settings["cookie_secret"]을 설정해주고 있는것을 볼 수 있다.

 

소스코드를 보면 class RequestHandler의 설정값인 application.settings의 딕셔너리에 cookie_secret을 담고 있는것을 확인할 수 있다.

 

그리고 auth.py를 살펴보자

 

 

 

auth.py에도 cookie_secret이 존재하는데 위에 보면 아까 봤던 RequestHandler class를 'handler'라는 이름으로 할당해준것을 확인할 수 있따.

 

한마디로 self.application.settings를 살펴보려면 handler.application.settings.cookie_secret을 살펴보면 된다는 뜻.

 

근데 _(언더바)가 필터링이니 handler.application.settings 를 살펴보면 되시겠다.

 

 

나머진 ffffffflag인가 그 파일 md5 해시 맞춰주고 읽으면 끝

 

 

 

'Web > BUUCTF' 카테고리의 다른 글

[BUUCTF] - [强网杯 2019]高明的黑客1  (0) 2020.02.14
[BUUCTF] - [HCTF 2018]admin1  (0) 2020.02.13
[BUUCTF] - [SUCTF 2019]EasySQL1  (0) 2020.02.13
[BUUCTF] - [护网杯 2018]easy_tornado1  (0) 2020.02.13
[BUUCTF] - [强网杯 2019]随便注1  (0) 2020.02.12
[BUUCTF] - [HCTF 2018]WarmUp1  (2) 2020.02.11

+ Recent posts