Description
Start(KST): 2019/01/31 17:00
Duration: 85 hours
Final Rank: 84
WebCipher
Category: Scripting/Coding
Points: 300
Description:
To verify that only computers can access the website, you must reverse the Caesar cipher There are a list of possible words that the cipher may be here
https://challenges.neverlanctf.com:1160
Point
- Caesar cipher를 이해하고 복호화 할 수 있는가
Write-up
문제 사이트로 들어가보면, Caesar Cipher를 이용해서 암호화된 문자열이 주어져 있다.
Caesar Cipher는 subsitutuin cipher 중의 하나로, 일반적으로 알파벳 n번째 문자를 n + k % 26번째 문자로 치환하는 방법으로 사용한다.
주어진 암호문은 jllnunajcxa인데, k를 모르니 적당한 문자가 나올 때까지 k를 바꿔가며 평문을 구해보자.
연습삼아 손으로 해보는 것도 좋겠지만 조금만 검색해보면 수많은 온라인 도구들을 찾아볼 수 있다.
k값에 따라 다양하게 decryption 된 문자열들 중에 하나를 골라보자. 문제에서 list of possible words가 있다고 했으니 뭔가 의미가 있을 것 같은 값으로 accelerator를 골라서 제출하면 flag를 얻을 수 있다.
Flag : flag{H3llo_c4es3r}
References
- Caesar Cipher Online Decryptor: https://www.dcode.fr/caesar-cipher
- Online en/decoder: https://cryptii.com/pipes/caesar-cipher
Unexpected intruder
Category: Recon
Points: 50
Description:
occurring in Chicago, Illinois, United States, on the evening of November 22. There was an interruption like nothing we had ever seen before.
What was the name of the Intruder?
Point
- 인터넷 검색을 할 수 있는가?
Write-up
11월 22일 저녁에 시카고에서 interruption이 일어났었다고 한다. 침입자의 이름을 알아보자.
Chicago November 22 intruder로 검색해보면 1987년 11월 22일에 있었던 Max Headroom의 공중파 방송 탈취 사건에 대한 내용을 쉽게 찾을 수 있다.
Max Headroom을 submit 해보았더니 flag가 아니어서, MaxHeadroom으로 붙여서 넣었더니 포인트를 획득할 수 있었다.
Flag : MaxHeadroom
References
- Max Headroom on wiki: https://en.wikipedia.org/wiki/Max_Headroom_broadcast_signal_intrusion
purvesta
Category: Recon
Points: 75
Description:
I love Github. Use it all the time! Just wish they could host a webpage...
Point
- GitHub에서 특성에 맞는 repository를 찾을 수 있는가
Write-up
CTF에서 잘 찾아보기 힘든 Recon 카테고리의 문제이다. Recon은 reconnaissance(정찰)의 약자로, 사회망 등의 검색을 통해 얻은 정보를 이용하여 푸는 문제인 것 같다.
출제자는 깃허브를 매우 좋아한다고 한다. 별다른 힌트가 없는 것 같으니 일단 깃허브에서 문제 제목인 purvesta를 검색해 보자.
purvesta라는 사람의 계정이 검색되는데, 들어가 보면 본인이 NeverLAN CTF의 cofounder라고 친절하게 소개하고 있다.
이 계정에는 public repository 8개가 있는데, 문제에서 webpage 언급이 있으니 그 중에 purvesta.github.io로 들어가본다. ([account].github.io 저장소는 http webserver 기능을 제공한다.)
purvesta.github.io 저장소에는 파일이 달랑 3개가 있는데, 가장 수상해 보이는 lol 파일을 열어보면 flag를 발견할 수 있다.
Flag : flag{Th1s_g1thub_l00ks_a_l1l_sparc3}
References
- (없음)
Filling a need
Category: Recon
Points: 100
Description:
This organizations creation was announced Mon Sep 24 2001
What is the full name of the organization?
Point
- 인터넷 검색을 할 수 있는가?
Write-up
2001년 9월 24일에 설립이 발표된 기관은 무엇인지 찾아보자.
처음엔 그날 설립된 기관으로 알고 검색하여 INTERPOL을 submint 했으나 아니었다.
문제를 다시 읽어보니, 설립된 날이 아니라 설립이 발표된 날이었다.
Mon Sep 24 2001 announced organization으로 검색한 결과, OWASP history 페이지가 나왔는데 OWASP도 flag가 아니었다.
잘못짚은건가 싶어서 내용을 좀 더 들여다 보았더니 아래와 같은 내용이 있었다.
Date: Mon Sep 24 2001 - 01:52:35 CDT
(...중략...)
So we are pleased to announce the creation of the "Open Web Application Security Project" known as OWASP.
OWASP 대신 full name인 OpenWebApplicationSecurityProject을 submit 했더니 포인트를 줬다.
Flag : OpenWebApplicationSecurityProject
References
- History of OWASP: https://www.owasp.org/index.php/History_of_OWASP
It's to KeyZ
Category: Recon
Points: 250
Description:
It looks like N30 has been keeping passwords secret with some software he wrote, but he should know better than to rely on proprietary software for security.
It looks like he left the repo public too!
Point
- 주어진 정보로부터 추가적인 개인 정보를 수집할 수 있는가
- C++ 코드를 읽을 수 있는가
Write-up
잘 모르겠으니 일단 주어진 파일을 좀 살펴보자.
맨 앞에 있는 것은 매직 코드와 헤더인것 같고, 00이 한참을 이어지다가 0x04, 0x1f, 그리고 flag라는 문자열과 정체모를 31 바이트 데이터가 있다.
데이터 파일의 전형적인 Length - Type(Header or Key) - Value 구조인 것 같다. 그 말은 "flag" 이후에 나오는 31 바이트를 어떻게든 풀어야 한다는 뜻이다.
이리저리 xor를 시도해 보았으나 성공적인 결과는 얻지 못했다. 역시 카테고리가 Recon인 만큼, 검색을 통한 추가 데이터 입수가 필수인 듯 하다.
문제 지문을 유심히 읽어보면 N30이라는 사람의 public repo를 찾아야 하는 것 같다.
구글신의 힘을 빌려 N30 keyz를 키워드로 검색해 보았으나 별다른 소득이 없었다.
이리저리 사이트를 뒤적이다가 NeverLAN CTF의 홈페이지에 가보니 About - THE CREATORS 메뉴에 N30에 대한 정보가 있었다.
Linked in의 정보에 따르면 N30의 이름은 Zane Durkin이라고 한다. Zane Durkin KeyZ로 다시 한번 검색을 해보면 구글이 KeyZ를 Key로 마음대로 수정해서 검색하는 바람에 제대로 된 결과를 찾기 어렵다.
Zane Durkin Github로 다시 한번 검색해보면 N30의 Github 계정을 찾을 수 있다. 감사한 마음으로 들어가보면 KeyZ라는 저장소를 볼 수 있고, key.cpp 파일을 통해 .keyz 파일의 구성을 비로소 확인할 수 있다.
key.cpp 코드를 쭉 살펴 보면 passwords.keyz 파일의 마지막 31 바이트는 특별히 암호화 된 것이 아니라 그냥 smazz라는 것을 이용해서 압축해둔 것인걸 알 수 있다.
같은 저장소에서 smazz.cpp 파일을 받아 우리가 궁금한 데이터를 얻어내는 main() 함수를 아래와 같이 추가하여 컴파일한 후 실행해보자.
int main(void)
{
char *comp = "\x2c\x89\x3b\xfe\x7b\x5a\x26\xff\x02\x37\x5f\x31\x03\x0a\xfe\x5f\x3c\x0c\xfe\x30\x3c\x0c\xfe\x31\x02\xc8\x0c\x53\xfe\x7d\x31";
char decompressed[1024];
int l = smaz_decompress(comp, 31, decompressed, 1024);
printf("%s\n", decompressed);
return 0;
}
압축 해제 결과, 25 byte의 flag를 얻을 수 있었다. 나머지 6 byte는 그냥 쓰레기 데이터인가보다.
Flag : flag{bu7_1ts_pr0pr1etary}
References
- (없음)
Binary 1
Category: Binary
Points: 100
Description:
A user accidentally installed malware on their computer and now the user database is unavailable. Can you recover the data and the flag?
Flag is All Caps
Point
- 문자열 인코딩을 다룰 수 있는가
Write-up
제공된 파일을 열어서 살펴보자.
한 줄에 60개쌕의 문자열이 있는데, [0-9a-f]로만 이루어진 hexa string이다.
두 글자씩 모아서 파싱해보면 ascii code가 나오고, 그에 맞는 문자열들을 다시 모아보면 base64 문자열인 것을 알 수 있다.
base64 디코딩을 하려고 하면 제대로 된 base64 문자열이 아니라고 하는데, 모든 라인들을 하나로 모아서 다시 디코딩을 시도하면 정상적으로 디코딩이 되고, 이번엔 json 문자열을 얻을 수 있다.
이 json 문자열을 파싱해보면 에러가 발생하는데, 처음에 빈 콤마(,)가 존재하기 때문이다. 이 콤마를 삭제하고 다시 json 파싱을 하면 제대로된 tree형 문자열 데이터를 얻을 수 있다.
간단한 python 코드를 작성해서 이 tree를 순회해보면 "flag"가 포함된 value를 발견할 수 있다.
Flag : flag{ENC0D1NG_D4TA_1S_N0T_ENCRY7I0N}
References
- (없음)
Binary 2
Category: Binary
Points: 200
Description:
Our lead Software Engineer recently left and deleted all the source code and changed the login information for our employee payroll application. Without the login information none of our employees will be paid. Can you help us by finding the login information?
***Flag is all caps
Point
- .Net decompile을 할 수 있는가
Write-up
주어진 exe 파일의 정보를 간단히 확인해보자.
32bit .Net executable이다.
.Net executable은 IDA 보다는 .Net전용 디컴파일러를 사용하면 훨씬 깔끔하게 볼 수 있다.
적당한 .Net 디컴파일러 중의 하나인 JetBrains dotPeek을 이용해서 디컴파일을 시도해 보았다. (그냥 파일을 열고 네비게이터에서 여기저기 살펴보면 바로 코드를 볼 수 있다.)
employee_payroll.cs 파일을 보면 login을 클릭했을 때 Username과 Password를 검사하는 함수로 각각 checkUsername()과 checkPassword() 함수를 호출하도록 되어있다.
private void btnLogin_Click(object sender, EventArgs e)
{
if (this.checkUsername() && this.checkPassword())
{
StringBuilder stringBuilder = new StringBuilder();
char ch = Convert.ToChar(this.r1);
stringBuilder.Append(ch.ToString());
ch = Convert.ToChar(this.r2);
stringBuilder.Append(ch.ToString());
(...중략...)
private bool checkUsername()
{
return this.txtUsername.Text == "admin";
}
private bool checkPassword()
{
return this.txtPassword.Text == "dGhpc19pc19ub3RfdGhlX2ZsYWdfeW91X3NlZWtfa2VlcF9sb29raW5n";
}
이 함수들을 확인해보면 로그인에 필요한 id와 password를 바로 알 수 있고, 알아낸 값들로 로그인을 시도해보면 flag를 얻을 수 있다.
Flag : flag{ST0RING_STAT1C_PA55WORDS_1N_FIL3S_1S_N0T_S3CUR3}
References
- JetBrains dotPeek: https://www.jetbrains.com/decompiler/
Binary 3
Category: Binary
Points: 300
Description:
Another day, another disgruntled engineer. It seems that the login is working fine, but some portions of the application are broken. Do you think you could fix the the code and retrieve the flag?
Point
- ELF binary decompile 및 분석을 할 수 있는가
- debugger를 이용해서 임의의 함수를 실행할 수 있는가
Write-up
file 명령을 이용하면 ELF 64bit executable인 것을 알 수 있다. 바로 실행 시켜보면 username과 password를 받는데, username이 정확해야 password를 넣을 수 있지만 두어번 실행해보면 username은 admin인 것을 알 수 있다.
더이상 알아낼만한 것이 없으니 리버싱을 시작해보자.
IDA로 열어서 decompile 후 main함수부터 따라들어가 보면 c(), u(), b() 정도의 함수들을 호출하고 username/password를 체크하는데, 로그인 후에 딱히 하는 행동이 없다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
c();
if ( (unsigned int)u() )
{
if ( (unsigned int)b() )
puts("you are now logged in...");
else
puts("password incorrect.");
result = 0;
}
else
{
puts("username incorrect.");
result = 0;
}
return result;
}
그러고 보면 c(), u(), b() 함수 외에도 함수들이 있는데, 어디서 호출되는건지 잘 모르겠다.
f() 함수부터 살펴보면, 뭔가 값을 만드는 함수인 것 같은데, x()라는 함수가 호출한다. x() 함수는 순서대로 f(), l(), a(), g(), s() 함수를 호출한다.(!!)
x() 함수는 d() 함수에서 호출하는데, d() 함수는 따로 호출해주는 함수가 없다.
d() 함수의 코드를 살펴보면, 특정한 url 주소를 만들어서 http request를 날리고, response를 파싱해서 결과를 출력해주는 함수로 보인다.
별달리 argument를 받는 것도 아닌 것 같으니, gdb를 이용해서 d() 함수를 호출해 보자.
바로 flag를 얻을 수 있었다.
Flag : flag{AP1S_SH0ULD_4LWAYS_B3_PR0T3CTED}
References
- (없음)
Binary 4
Category: Binary
Points: 500
Description:
It appears we're not doing a very good job at employee retention and statisfaction. Now our Embedded Firmawre Engineer has left and removed the source code for our latest IoT firmware. Please help us to recover the source code for the following hex file. We would prefer C, but assembly would work as well. I also think there's a flag somewhere in the sorce for you.
Point
- hex 형식 파일을 이해하고 파싱할 수 있는가
- 실행 바이너리에 대한 target을 식별하고 분석할 수 있는가
Write-up
사람을 얼마나 갈궜으면 선량한 개발자가 소스코드를 다 지워버리고 도망갔을까
악의 무리를 돕는 것은 내키지 않지만, 배점이 꽤 높으니 분석을 시작해보자.
주어진 파일을 열어보면, 의미는 둘째 치고 일단 읽을 수 있는 ascii text 파일이다.
딱 봐도 뭔가 형식에 맞춰 쓰여진 데이터 파일인 것 같다. ".hex 파일 형식"으로 검색을 해보면 인텔 HEX에 대한 내용이 가장 먼저 나온다.
ASCII 텍스트 형식으로 이진 정보를 전달하는 파일 형식이다. 라고 하니, 주어진 파일의 형식이 이게 맞는 것 같다.
파싱하는 구조에 대해서 설명이 나와있으니, 간단히 파싱하는 코드를 만들어서 바이너리 파일을 만들어 보자
hex 파일이 특별히 변조되거나 한 부분도 없고, 파싱도 제대로 된 것 같은데, file 명령을 이용해도 별달리 파일 형식이 인식되지 않는다.
혹시나 싶으니 strings를 이용해서 어떤 문자열들이 있는지 확인해보자.
Base64 인코딩 된 것으로 보이는 문자열이 있다. 이런건 바로 디코딩 해봐야 한다.
이거 아니랜다.
그 아래로는 Arduino라는 문자열도 보인다. Arduino용 이미지였나 보다.
Arduino에 대해서 간단히 검색해보면 Wiki 페이지에 Atmel AVR CPU를 사용한다는 정보가 나온다.
IDA를 이용해 바이너리를 열면서 CPU로 Atmel AVR를 지정해서 로딩해보자.
함수 몇개가 발견된 것을 알고 있는데, 쭉 훑어 내려가다 보면 sub_30E 함수에서 한 글자씩 메모리에 집어넣는(것 같은) 아래와 같은 코드가 있다. 순서대로 보면 마지막에 "="를 두개 넣는다. NeverLAN CTF 출제자들은 Base64를 굉장히 좋아하는 것 같다.
ROM:0390 loc_390: ; CODE XREF: sub_30E+18A↓j
ROM:0390 ldi r24, 0x5A ; 'Z'
ROM:0391 ldi r25, 0
ROM:0392 call sub_1A1
ROM:0394 ldi r24, 0x6D ; 'm'
ROM:0395 ldi r25, 0
ROM:0396 call sub_1A1
(...중략...)
ROM:0444 ldi r24, 0x51 ; 'Q'
ROM:0445 ldi r25, 0
ROM:0446 call sub_1A1
ROM:0448 ldi r24, 0x3D ; '='
ROM:0449 ldi r25, 0
ROM:044A call sub_1A1
ROM:044C ldi r24, 0x3D ; '='
ROM:044D ldi r25, 0
ROM:044E call sub_1A1
복사해 넣는 값들을 하나하나 모아서 Base64 디코딩을 해보니 flag를 얻을 수 있었다.
Flag : flag{ST1LL_US1N6_ST47IC_P455WORDS}
References
- 인텔 HEX: https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%85%94_HEX
- 아두이노: https://ko.wikipedia.org/wiki/%EC%95%84%EB%91%90%EC%9D%B4%EB%85%B8
Cover the BASEs
Category: Crypto
Points: 25
Description:
ZmxhZ3tEMWRfeTB1X2QwX3RoM19QcjNfQ1RGfQ==
Point
- base64 인코딩된 문자열을 인식하고 디코딩할 수 있는가
Write-up
base64 인코딩 된 문자열이 주어진다.
일단 다른거 제쳐놓고 == 로 끝나는 시점에서 99%라고 보면 된다.
온라인 base64 en/coding 도구들도 널려있지만 아까 리눅스 터미널을 열어둔게 있으니 간단하게 python으로 확인해보자.
한번 디코딩 했더니 바로 flag가 나왔다. 보통은 그래도 두번은 인코딩 하던데
Flag : flag{D1d_y0u_d0_th3_Pr3_CTF}
References
- (없음)
Alphabet Soup
Category: Crypto
Points: 125
Description:
MKXU IDKMI DM BDASKMI NLU XCPJNDICFQ! K VDMGUC KW PDT GKG NLKB HP LFMG DC TBUG PDTC CUBDTCXUB. K'Q BTCU MDV PDT VFMN F WAFI BD LUCU KN KB WAFI GDKMINLKBHPLFMGKBQDCUWTMNLFMFMDMAKMUNDDA
Point
- 알파벳에 대한 전치암호를 이해하고 풀 수 있는가
Write-up
제목이 Alphabet Soup인걸 보니 알파벳을 마구 섞어놨나보다. A-Z to A-Z의 매칭 관계를 알아내야 이 문장을 풀어낼 수 있을 것이다. 쉽지 않겠지만 하나씩 한번 풀어보자.
문장을 잘 보면 혼자있는 K와 K'Q를 찾을 수 있다. 높은 확률로 K는 i로 매칭되고, Q는 m으로 매칭될 것 같다. (매칭된 문자들을 구분하기 위해 변환된 알파벳은 소문자로 썼다.)
매칭을 적용하여 아래 문자열을 얻었다.
MiXU IDiMI DM BDASiMI NLU XCPJNDICFm! i VDMGUC iW PDT GiG NLiB HP LFMG DC TBUG PDTC CUBDTCXUB. i'm BTCU MDV PDT VFMN F WAFI BD LUCU iN iB WAFI GDiMINLiBHPLFMGiBmDCUWTMNLFMFMDMAiMUNDDA
마지막에 좀 긴 문자열이 flag일 거라고 추측할 수 있고, 그 앞에 iN iB는 it is일 것 같아서 N:t와 B:s의 매칭을 추가했다.
MiXU IDiMI DM sDASiMI tLU XCPJtDICFm! i VDMGUC iW PDT GiG tLis HP LFMG DC TsUG PDTC CUsDTCXUs. i'm sTCU MDV PDT VFMt F WAFI sD LUCU it is WAFI GDiMItLisHPLFMGismDCUWTMtLFMFMDMAiMUtDDA
그 다음 부터는 조금 막막해서 검색을 통해 영단어 데이터를 다운로드 받았다.
지금까지 얻어진 문자열 중에서 tLis가 4글자 중에 1글자만 알아내면 되는거라서 영단어 데이터에서 4글자이고 t?is와 매칭되는 단어들을 찾았는데, this가 가장 그럴듯해 보여 L:h의 매칭을 추가했다.
MiXU IDiMI DM sDASiMI thU XCPJtDICFm! i VDMGUC iW PDT GiG this HP hFMG DC TsUG PDTC CUsDTCXUs. i'm sTCU MDV PDT VFMt F WAFI sD hUCU it is WAFI GDiMIthisHPhFMGismDCUWTMthFMFMDMAiMUtDDA
이렇게 해두고 보니 F WAFI sD hUCU 부분이 눈에 띈다.
F는 혼자 있는걸 보니 I 또는 a 인것 같은데, I는 아까 나왔으니 F:a 매칭을 추가했다. hUCU는 한글자 밖에 모르지만 U가 반복되는 패턴때문에 영단어 패턴 검색 결과 here로 간주, U:e, C:r 매칭도 추가하였다.
MiXe IDiMI DM sDASiMI the XrPJtDIram! i VDMGer iW PDT GiG this HP haMG Dr TseG PDTr resDTrXes. i'm sTre MDV PDT VaMt a WAaI sD here it is WAaI GDiMIthisHPhaMGismDreWTMthaMaMDMAiMetDDA
here it is 앞에 sD는 so가 되는게 자연스러울 것 같으니 D:o를 추가하고, sTre는 s?re에 맞는 단어 중 i'm 뒤에 오는게 자연스러운 sure로 추측하여 T:u를 추가하였다.
MiXe IoiMI oM soASiMI the XrPJtoIram! i VoMGer iW Pou GiG this HP haMG or useG Pour resourXes. i'm sure MoV Pou VaMt a WAaI so here it is WAaI GoiMIthisHPhaMGismoreWuMthaMaMoMAiMetooA
그러고 나서 i VoMGer iW Pou GiG 부분을 봤는데, 이 부분이 I wonder if you did인것 같은 감이 강하게 와서, V:w, M:n, G:d, W:f, P:y를 추가하였다.
niXe IoinI on soASinI the XryJtoIram! i wonder if you did this Hy hand or used your resourXes. i'm sure now you want a fAaI so here it is fAaI doinIthisHyhandismorefunthananonAinetooA
이제 어느 정도 쉽게 유추가 가능한데, niXe에서 X:c를, Hy hand에서 H:b를 추가했다.
nice IoinI on soASinI the cryJtoIram! i wonder if you did this by hand or used your resources. i'm sure now you want a fAaI so here it is fAaI doinIthisbyhandismorefunthananonAinetooA
doinIthis에서 I:g를 추가하면 첫 단락이 nice going on soASing the cryJtogram!이 되는데, 여기서 A:l, S:v, J:p를 추가할 수 있다.
여기까지 추가하면 모든 문자열을 변환할 수 있게 되어 아래와 같은 문장을 얻는다.
nice going on solving the cryptogram! i wonder if you did this by hand or used your resources. i'm sure now you want a flag so here it is flag doingthisbyhandismorefunthananonlinetool
Flag : doingthisbyhandismorefunthananonlinetool
flag를 읽고 보니 onlinetool을 이용했으면 더 쉽게 찾았을텐데..하는 생각이 들었다.
변환 코드는 첨부해 두었다.
References
- english words: https://github.com/dwyl/english-words
Cookie Monster
Category: Web
Points: 20
Description:
It's a classic https://challenges.neverlanctf.com:1110
Point
- cookie를 확인하고 변조할 수 있는가
수수께끼를 잘 풀 수 있는가
Write-up
문제 사이트에 들어가보면 He's my favorite Red guy라는 메세지만 표시되고 페이지 소스를 확인해봐도 별다른 것이 보이지 않는다.
문제 이름에 cookie가 들어가 burp suite를 이용해서 request와 response의 cookie를 확인해보자
request에 Set-Cookie 항목이 있고, 이로 인해 cookie에 Red_Guy's_name=NameGoesHere가 추가된 것을 확인할 수 있다.
여기에 내이름이나 문제명 등을 넣어서 request를 날려보았지만 별다른 변화를 확인할 수 없었다.
한참을 고민하다가 문제 제목인 Cookie Monster를 검색해 보았더니 동명의 어린이 애니메이션이 있었다.
여기서 붉은 캐릭터의 이름이 엘모(elmo)라서 Red_Guy's_name=elmo로 request를 날렸더니 flag를 얻을 수 있었다.
Flag : flag{YummyC00k13s}
References
- (없음)
React To This
Category: Web
Points: 50
Description:
It looks like someone set up their react site wrong...
https://challenges.neverlanctf.com:1145
Point
- javascript 코드를 읽고 이해할 수 있는가
Write-up
문제 사이트를 열어보자.
별다른 입력창 같은것도 없고 특별한 것들도 잘 보이지 않는다.
소스 코드를 보면 아래와 같이 javascript를 include 하는 부분이 있는 것을 알 수 있다.
<script type="text/javascript" src="/static/js/main.237378d2.js"></script>
chrome의 개발자 도구를 열어서 소스코드를 확인해보자. main.237378d2.js 외에도 몇가지 js들을 더 볼 수 있는데, 그 중 app.js파일에 import Admin from './Pages/Admin.js' 라는 명령이 있는 것이 보인다.
source tree에 보면 Pages 디렉토리가 있고 그 아래 Admin.js가 있길래 코드를 확인해 보았더니 flg 변수와 함께 flag를 출력해주는 루틴을 확인할 수 있었다.
Flag : flag{s3cur3_y0ur_s3ss10ns}
References
- (없음)
Dirty Validate
Category: Web
Points: 50
Description:
To keep my server from doing a lot of work, I made javascript do the heavy lifting of checking a user's password
https://challenges.neverlanctf.com:1135
Point
- javascript 코드를 읽고 이해할 수 있는가
Write-up
문제 사이트에 들어가보면 로그인 할 수 있는 화면이 보인다.
Username에 대충 아무거나 넣어보려고 하는데, 다 넣지도 않았는데 Username is incorrect라는 메세지가 나온다.
코드 소스를 확인해보자.
Username 쪽에 key 입력 event가 발생할 때마다 특정 동작을 하도록 되어있다. 좀 더 자세히 들여다보자.
// For element with id='name', when a key is pressed run this function
$('#name').on('keypress',function(){
// get the value that is in element with id='name'
var that = $('#name');
// make an ajax request to get the expected username
$.ajax('webhooks/get_username.php',{})
.done(function(data)
{ // once the request has been completed, run this function
data = JSON.parse(data);
if( data.usernames.indexOf(that.val()) != -1 ){ // see if the username is in the list
that.css('border', '1px solid green'); // if it matches turn the border green
$('#output').html('Username is correct'); // state that the user was correct
}else{ // if the user typed in something incorrect
that.css('border', ''); // set input box border to default color
$('#output').html('Username is incorrect'); // say the user was incorrect
}
키 입력이 발생하면 webhooks/get_username.php를 호출하고 response로 받아온 json파일에 있는 이름인지 확인하도록 되어있다.
https://challenges.neverlanctf.com:1135/webhooks/get_username.php 에 브라우저로 접속해 보면 아래와 같은 응답을 확인할 수 있다.
{"usernames":[ "AdamsFamily","Mr. Clean","Dr. Whom","JimmyOneShoe" ] }
이 네 가지 username 중의 하나로 로그인 하면 되는 것 같다.
Username에 AdamsFamily를 넣고 Password에 아무거나 넣어보면 이번에도 Password is incorrect 메세지가 나온다.
이 부분도 코드를 확인해보자.
// For the password input now
// This is a BAD idea, never validate sensitive data in javascript
$('#pass').on('keypress', function(){
// get value for element with id='pass'
var that = $('#pass');
// make an ajax request to get the expected password for the given username
$.ajax('webhooks/get_password.php?user='+encodeURIComponent($('#name').val()),{})
.done(function(data)
{// once the request has completed, run this function
// remove whitespace from data
data = data.replace(/(\r\n|\n|\r)/gm,"");
// check if the data matches the given value
if(window.atob(data) == that.val()){
// if value is correct, show a green border
that.css('border', '1px solid green');
$('#output').html(window.atob(data));
}else{
// if value is false, remove border
that.css('border', '');
$('#output').html('Password is incorrect');
}
이번에는 내가 입력한 username을 이용하여 get_password.php에 request를 던진다.
받아온 데이터는 특수문자를 거르고, atob() 함수를 이용해서 base64 decoding을 한 후, 내가 입력한 password와 비교하도록 되어있다.
password 정보가 client까지 날아오니 이걸 받아서 확인해보자.
https://challenges.neverlanctf.com:1135/webhooks/get_password.php?user=AdamsFamily 와 같은 방법으로 각각의 username에 대한 response를 받아보면 4가지의 base64 문자열을 얻을 수 있고, 이를 디코딩하여 다음과 같은 값들을 얻을 수 있다.
치사하게 하나만 flag고 나머지 3개는 꽝이다.
Flag : flag{D0n't_7rus7_JS}
References
- (없음)
Things are not always what they seem
Category: Web
Points: 50
Description:
if you can't find it you're not looking hard enough
https://challenges.neverlanctf.com:1165/hello.html
Point
- 웹페이지의 소스코드를 확인할 수 있는가
Write-up
문제 사이트에 들어가보아도 별다른 특이점이 보이지 않는다.
사이트의 소스코드를 살펴보자.
flag를 얻었다.
문제 사이트에서 전체선택(ctrl + a)을 해봐도 flag를 확인할 수 있었다.
Flag : flag{Whale_w0u1d_y0u_l00k3y_th3r3}
References
- (없음)
SQL Fun 1
Category: Web
Points: 75
Description:
REPORT: 'My Customer forgot his Password. His Fname is Jimmy. Can you get his password for me? It should be in the users table'
https://challenges.neverlanctf.com:1150
Point
- select, from, where 등을 이용해 sql문을 구성할 수 있는가
Web 문제의 함정에 빠지지 않을 수 있는가
Write-up
문제 페이지에 들어가보면 무엇인가 텍스트를 제출할 수 있도록 되어있다.
admin' or '1'='1 등을 이용해서 sql injection을 시도해 보았으나 동작하지 않았다.
WHERE 절이 반드시 포함된다고 하여 where 절을 넣어봤으나 별다른 특이점을 찾지 못했고, 이런저런 시도를 하다가 select 1 from users #where를 넣어봤을 때 sql injection이 아니라 그냥 sql문을 작성하면 되는 문제인 것을 알았다.
그래서 문제에 주어진대로 아래와 같은 SQL query를 작성해서 입력했더니 flag를 얻을 수 있었다.
select Password from users where Fname="Jimmy"
Flag : flag{SQL_F0r_Th3_W1n}
References
- (없음)
SQL Fun 2
Category: Web
Points: 75
Description:
REPORT: A Client forgot his Password... again. Could you get it for me? He has a users account and his Lname is Miller if that helps at all. Oh! and Ken was saying something about a new table called passwd; said it was better to separate things
https://challenges.neverlanctf.com:1155
Point
- join을 이용하여 sql query를 작성할 수 있는가
Write-up
users 테이블 외에 passwd 테이블도 이용해서 password를 찾으라고 한다. 이럴때 join 문을 쓰는거라고 배웠으니 의식의 흐름대로 join문을 검색해서 query를 작성하자.
우선 select * from users를 이용해서 users 테이블의 구조를 확인한다.
마찬가지 방법으로 구한 passwd table의 구조는 다음과 같다.
(여기서 그냥 user_id 5번의 passwd를 base64 디코드해도 되지만 연습을 위해 일단 그냥 진행하자..)
양 테이블에 id가 공통이니 아래와 같이 sql query를 보내면 password를 얻을 수 있다.
select Password from users join passwd on users.id=passwd.user_id where Lname="Miller"
획득한 Password를 base64 디코딩하면 flag를 얻을 수 있다.
Flag : flag{W1ll_Y0u_J01N_M3?}
References
- (없음)
Console
Category: Web
Points: 75
Description:
You control the browser
https://challenges.neverlanctf.com:1120
Point
- browser의 개발자 도구를 이용하여 javascript를 실행할 수 있는가
Write-up
무엇인가 텍스트를 입력받을 수 있는 박스가 주어진다.
아무거나 넣어보면 Nope, try again이라는 메세지를 보게 된다.
페이지 소스를 확인해 보자.
var foo = document.getElementById("p");
function what(){
var input = document.getElementById("pass").value;
if( md5(input) == "7b1ece53a46f4a5a2995b9cf901bf457" ){
getThat('Y');
}else{getThat('N')}
}
function getThat(strg){
if(strg == 'Y'){
// Note: There is no data sent to the key.php file...
// jquery ajax reference: https://api.jquery.com/jQuery.ajax/
$.ajax({
type: 'GET',
url: '1/key.php',
success: function (file_html) {
// success
foo.innerHTML=(file_html)
}
});
}else{
foo.innerHTML = "Nope, try again";
}
입력한 텍스트의 md5 해쉬가 7b1ece53a46f4a5a2995b9cf901bf457이면 getThat('Y') 호출에 의해 key를 찍어주는 코드인 것 같다.
우선 online md5 decryptor를 이용해서 7b1ece53a46f4a5a2995b9cf901bf457에 해당하는 원본 메세지를 찾으려고 했으나 찾을 수 없었고, 브라우저를 통해 1/key.php 파일에 직접 http request를 해보냈으나 결과를 확인할 수 없었다.
그래서 chrome의 개발자 도구를 이용하여 getThat('Y') 함수를 호출한 결과, flag를 확인할 수 있었다.
Flag : flag{console_controls_js}
References
- (없음)
Das Blog
Category: Web
Points: 125
Description:
Word on the street, Johnny's got a blog. Seems he doesn't know how to escape his inputs.
https://challenges.neverlanctf.com:1125
Point
- sql injection을 할 수 있는가
Write-up
문제 페이지에 들어가보면 login페이지로 가는 링크를 볼 수 있다.
아무거나 넣어서 로그인을 시도해보면 Username / Password가 잘못되었다고 나온다.
Username에 ' 를 넣어보면 user를 찾는데 문제가 생겼다는 메세지를 볼 수 있다.
개발자의 입장에서 생각했을 때, 사용자 정보 DB에 대한 query가 실패한 케이스임을 가정해 볼 수 있을 것 같다. query는 성공했으나 적절한 사용자 정보를 확인하지 못한 경우가 먼저의 케이스 였을 것 같다.
' 를 넣었을 때 query가 실패한걸 보면 이건 진짜 SQL injection 문제인걸로 보인다.
일단 admin' or '1'='1을 넣어보자. 쿼리 자체는 성공한 듯 싶으나 admin이라는 이름의 계정이 없는 것 같다. 혹시나 싶어 admin 대신에 flag나 neverlan 같은 것을 넣어 보았지만 여전히 유효하지 않은 계정이었다.
계정 정보를 어디서 얻을 수 있을까 싶어 문제 사이트를 다시 살펴보다가 다시 들른 메인 페이지에 이름이 있어서 그걸 이용하여 Johnny' or '1'='1을 넣어봤더니 로그인이 되었다.
주어지는 링크를 따라 메인페이지로 돌아갔더니 flag를 얻을 수 있었다.
Flag : flag{3sc4pe_Y0ur_1npu7s}
References
- (없음)
Das Blog 2
Category: Web
Points: 150
Description:
Well, we really showed Johnny. It looks like he made some changes... But he still isn't escaping his inputs. Teach him a lesson.
https://challenges.neverlanctf.com:1130
Point
- 서버의 sql query 구성을 유추할 수 있는가
- 서버의 sql query 구성에 따라 union select를 이용한 sql injection 공격을 할 수 있는가
Write-up
Das Blog 문제와 동일한 취약점이 있는지 확인하기 위해서 Johnny' or '1'='1을 다시 한번 넣어보자.
정상적으로 로그인이 되지만, You are now logged in as Johnny with permissions user라고 나오고, 메인페이지로 가보아도 아까와는 달리 ADMIN 권한이 아니라 DEFAULT 권한으로 로그인 했다고 나온다.
그렇다면 admin이 있겠지 하는 생각에 admin' or '1'='1을 시도해 보았으나 로그인 할 수 없었다. 오류 메세지를 보면 admin 계정이 따로 있지는 않은 것 같다.
로그인 메세지와 admin 계정이 없는 점을 감안하면 사용자 계정 정보에 권한 정보가 딸려있는 것 같다. 그렇다면 사용자 테이블에 id와 permission이 있어서 각각 'Jhonny'와 'user'로 들어있고, sql query를 이용해서 (id, permission) 형태로 받아온게 아닐까 하는 생각이 든다.
이 생각이 맞다면 아마 서버의 sql 쿼리문은 아래와 같이 구성되어 있을 것이다.
select id, permission from users where id='{id}' and pwd='{pwd}'
이 쿼리의 결과로 permission에 'admin'이 들어가게 sql injection을 하면 될것 같다.
union select를 이용해서 아래와 같이 간단하게 작성해보자.
a' union select 'b', 'admin' #
union select는 select의 결과를 앞의 결과에 붙여주는 역할을 한다. (잘 붙이기 위해서는 column의 구성이 앞의 select 문과 동일해야 한다.)
맨 앞의 a는 users 테이블에 없을 것 같은 사용자 이름이고, b는 아무거나 넣어도 상관없다. 이 입력이 들어간 쿼리의 결과는 (b, admin)이 될 것이고, 그 결과 아래와 같은 로그인 메세지를 받을 수 있다.
링크를 따라 메인 페이지로 돌아가보면 flag를 확인할 수 있다.
Flag : •flag{Pwn3d_W1th_SQL}
References
- (없음)
Trivia 문제들
Category: Trivia
Points: 20 for each
Description:
Trivia 문제는 간단한 Quiz들로 구성되어있다. 부담없이 풀어보자.
Point
- 간단한 보안 상식 및 검색 능력을 갖고 있는가
Write-up
1. SQL Trivia 1
* Q: The oldest SQL Injection Vulnerability. The flag is the vulnerability ID.
* A: CVE-2000-1233
* Sol: 가장 오래된 SQL Injection 타입의 CVE ID를 찾아보자 (cvedetails.com - SQL Injection 검색 - CVE Number Ascending)
2. SQL Trivia 2
* Q: In MSSQL Injection Whats the query to see what version it is?
* A: SELECT @@VERSION
3. Sea Quali
* Q: A domain-specific language used in programming and designed for managing data held in a relational database management system, or for stream processing in a relational data stream management system.
* A: sql
4. 64 Characters
* Q: A group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation.
* A: base64
5. With Some Milk
* Q: A small piece of data sent from a website and stored on the user's computer by the user's web browser while the user is browsing.
* A: cookie
6. Beep Boop
* Q: A standard used by websites to communicate with web crawlers and other web robots. The standard specifies how to inform the web robot about which areas of the website should not be processed or scanned
* A: robots.txt
References
- 검색은 구글: https://google.com