[Clear] X-MAS CTF 2020 Santa's consolation Writeup
두 번째 Web문제였다. 특이한 점은 출제자측에서 곧 런칭할 버그바운티 사이트 안에 문제를 구축해놓았다. 홍보 겸 좋은 방법이라고 생각한다. (나도 이 문제를 계기로 구독하였다 ㅋㅋ)
Target에 접속해보면
아래와 같은 사이트가 뜨고, LET's HACK 버튼을 클릭하면 콘솔창에 문제가 로딩된다.
문제를 분석하기 위해 challenge.js 코드를 정리하면 아래와 같다.
console.log("%c██████╗░██╗░░░░░██╗░░░██╗██╗░░░██╗██╗░░██╗\n\██╔══██╗██║░░░░░██║░░░██║██║░░░██║██║░██╔╝\n██████╦╝██║░░░░░██║░░░██║██║░░░██║█████═╝░\n██╔══██╗██║░░░░░██║░░░██║██║░░░██║██╔═██╗░\n██████╦╝███████╗╚██████╔╝╚██████╔╝██║░╚██╗\n╚═════╝░╚══════╝░╚═════╝░░╚═════╝░╚═╝░░╚═╝\n","color: #5cdb95");console.log("🐢 Javascript Challenge 🐢");console.log("Call win(<string>) with the correct parameter to get the flag");console.log("And don't forget to subscribe to our newsletter :D");
function check(s) {
const k="MkVUTThoak44TlROOGR6TThaak44TlROOGR6TThWRE14d0hPMnczTTF3M056d25OMnczTTF3M056d1hPNXdITzJ3M00xdzNOenduTjJ3M00xdzNOendYTndFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYwRVRNOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOEZETXh3SE8ydzNNMXczTnp3bk4ydzNNMXczTnp3bk13RURmNFlEZnpVRGYzTURmMllEZnpVRGYzTURmeUlUTThoak44TlROOGR6TThaak44TlROOGR6TThCVE14d0hPMnczTTF3M056d25OMnczTTF3M056dzNOeEVEZjRZRGZ6VURmM01EZjJZRGZ6VURmM01EZjFBVE04aGpOOE5UTjhkek04WmpOOE5UTjhkek04bFRPOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOGRUTzhoak44TlROOGR6TThaak44TlROOGR6TThSVE14d0hPMnczTTF3M056d25OMnczTTF3M056d1hPNXdITzJ3M00xdzNOenduTjJ3M00xdzNOenduTXlFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYzRVRNOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOGhETjhoak44TlROOGR6TThaak44TlROOGR6TThGak14d0hPMnczTTF3M056d25OMnczTTF3M056d25NeUVEZjRZRGZ6VURmM01EZjJZRGZ6VURmM01EZjFFVE04aGpOOE5UTjhkek04WmpOOE5UTjhkek04RkRNeHdITzJ3M00xdzNOenduTjJ3M00xdzNOendITndFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYxRVRNOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOFZETXh3SE8ydzNNMXczTnp3bk4ydzNNMXczTnp3WE94RURmNFlEZnpVRGYzTURmMllEZnpVRGYzTURmeUlUTThoak44TlROOGR6TThaak44TlROOGR6TThkVE84aGpOOE5UTjhkek04WmpOOE5UTjhkek04WlRNeHdITzJ3M00xdzNOenduTjJ3M00xdzNOendITXhFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYza0RmNFlEZnpVRGYzTURmMllEZnpVRGYzTURmMUVUTTAwMDBERVRDQURFUg==";
const k1=atob(k).split('').reverse().join('');
return bobify(s)===k1;
}
function bobify(s) {
if (~s.indexOf('a') || ~s.indexOf('t') || ~s.indexOf('e') || ~s.indexOf('i') || ~s.indexOf('z'))
return "[REDACTED]"; ##### ..... (1)
const s1 = s.replace(/4/g, 'a').replace(/3/g, 'e').replace(/1/g, 'i').replace(/7/g, 't').replace(/_/g, 'z').split('').join('[]');
##### ..... (2)
const s2 = encodeURI(s1).split('').map(c => c.charCodeAt(0)).join('|');
##### ..... (3)
const s3 = btoa("D@\xc0\t1\x03\xd3M4" + s2);
##### .....(4)
return s3;
}
function win(x){
return check(x) ? "X-MAS{"+x+"}" : "[REDACTED]";
}
check 함수를 보면 k1와 bobify(flag) 가 일치해야 한다.
k1 값을 구한 후 bobify의 역연산을 수행하는 reversebobify라는 함수를 작성하면 문제를 해결 할 수 있을 것이다.
k1값은 k를 base64디코딩 후 뒤집은 값이다. 주어진공식이 있으니 쉽게 확인 할 수 있다.
값을 확인 후 reversebobify의 인자가 될 revs라는 변수에 넣어주도록 하자.
bobify함수를 간단히 분석해보면 아래와 같은 로직을 수행함을 알 수 있다.
(1). 인자에 a,t,s,i,z 가 포함되면 안된다.
(2). 4->a, 3->e, 1->i, 7->t, _->z 변환 후 문자들 사이에 []를 삽입한다.
(3). url인코딩 후 각 문자를 아스키코드(10진수단위)로 변환 후 | 로 구분하는 문자열을 만든다.
(4). 앞에 D@\xc0\t1\x03\xd3M4 를 더한 후 base64 인코딩을 한다.
그렇다면 우리는 reversebobify함수의 로직을 구상해볼 수 있다.
(1). revs인자를 base64디코딩 한 후 D@\xc0\t1\x03\xd3M4 값을 제거한다.
(2). | 를 기준으로 각 값을 아스키코드표를 기반으로 문자열로 변환 후 이어붙혀 문자열을 만든 후 url디코딩을 한다.
(3). []를 제거 후 a->4, e->3, i->1, t->7, z->_ 변환을 수행후 플래그 형식에 맞춰준다.
위 로직을 만족하는 reversebobify함수를 구현하면 아래와 같다.
function reversebobify(revs){
const s1 = atob(revs).split(atob("REDACTED0000"))[1];
console.log(s1);
const s2 = decodeURI(s1.split('|').map(c => String.fromCharCode(c)).join(''));
console.log(s2);
const s3 = s2.replace(/a/g, '4').replace(/e/g, '3').replace(/i/g, '1').replace(/t/g, '7').replace(/z/g, '_').split('[]').join('');
console.log("X-MAS{"+s3+"}");
}
실제로 수행하면 정상적으로 플래그를 획득 할 수 있다.
FLAG = X-MAS{s4n74_w1sh3s_y0u_cr4c1un_f3r1c17}