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

 

대충 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] - [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의 비번을 바꿀 수 있다.

 

 

 

 

 

 

 

 

또 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] - [护网杯 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] - [强网杯 2019]随便注1  (0) 2020.02.12
[BUUCTF] - [HCTF 2018]WarmUp1  (2) 2020.02.11

하.. 중국어라 번역이 잘 안돼서 좀 짜증나긴 하지만 그래도 괜찮다.

 

보이는가? 이 개발 번역이

 

허..

 

 

 

오 sqli..

 

 

오 필터링..

 

 

주요 문법들이 다 안된다.

 

 

만고의 sqli 삽질 끝에 뭔지 몰겠어서 찾아보니 아니 이럴수가..

 

 

앗... multi query라니..

 

존재만 알고 CTF나 문제로 한번도 나온적이 없어서 생각의 틀에 갇혀있었던 것 같다.

 

multi query에서 할 수 있는 공격은 찾아보니

 

preg_match로 필터링 하는것 중에 rename이 필터링 안되어 있어서

 

1. 현재 조회되고있는 테이블 명을 rename으로 tmp로 변경

2. flag가 담겨있는 테이블을 현재 조회되고있는 테이블명으로 변경

3. '||1# attack

 

이런 방법이 있었다.

 

근데 이보다 더 쉬운 방법이 있는데, mysql 자체에서 제공하는 prepared statement 기능을 이용하는 것이다.

 

출처 : mysqlkorea.com

 

 

 

'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] - [HCTF 2018]WarmUp1  (2) 2020.02.11

대망의 BUUCTF 라업 시작,,,

 

@posix의 소개로 중국 워게임 사이트를 알게 되었다.

 

본래 워게임 풀어도 귀찮아서 라업을 잘 안쓰는 나지만 CTF 분야 중 웹은 아카이빙 해둔 곳이 보기 드물기에 아주 반가운 사이트.

 

안그래도 워게임은 풀게 없고 CTF는 간헐적으로 열려서 굶주려있던 찰나에 잘된 것 같다.

 

앞에서부터 차례로 뿌시기 시작!!

 

다만 아직도 쓰기 귀찮은건 여전하니 간략히 내 기억을 remind 할 수 있을 정도로만 적고 넘어가겠다.

 

 

각설하고

 


 

왜인지 첫문제부터 난이도가 좀 있어서 당황했다. 1595명이나 solving 했다는데,, 역시 중국의 맨파워는 대단한건가

 

<?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }

            if (in_array($page, $whitelist)) {
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }

    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?>

 

핵심은 $whitelist 로 source.php나 hint.php만 받고 몇가지 필터링을 통과하면 include로 LFI(Local File Inclusion)할 수 있는것.

 

나중에 알았는데 phpmyadmin에서 발생했던 1day 라고 하는것 같다.

 

보통 substr을 쓸텐데 mb_substr을 쓰고 urldecode -> mb_substr을 하는게 힌트라면 힌트.

 

 

 

?file=hint.php%253f/../../../../../../etc/passwd

 

LFI 성공!

'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

+ Recent posts