CTF

2022 Hacking Land CTF write-up

name2965 2022. 8. 1. 23:24
728x90

7월 30일경에 별생각 없이 유튜브를 들어가보니

 

노말틱님의 유튜브에서 자체적인 CTF를 개최한다는 영상을 봤습니다.

 

마침 저의 실력이 어느정도인지 궁금하기도 했고, 한번 이런 CTF도 나가보고 싶었어서

 

초보자인 저로서는 굉장히 혹한 대회였습니다.

 

그래서 카페에 가입하고나서 등록을 해서 CTF를 참가하게 되었습니다.

 

 

순위권에 들수 있으면 좋겠다는 생각은 했지만

운좋게도 제가 순위권에 들고말았습니다.

 

비록 작은 대회이지만 첫 CTF에서 순위권에 들수 있었던 것이 좋은 경험이었던것 같습니다.

 

 

Web

 

1. Pac-man

 

 

사이트에 들어가보면 평범한 팩맨 게임을 할 수 있는 사이트로 보입니다.

F12 키가 안먹기 때문에 우클릭을 해준뒤 개발자도구를 열어서 분석을 진행했습니다.

 

 

웹사이트의 소스를 살펴보면 /static/game.js /static/index.js 파일이 웹사이트에서

게임이 돌아가게하는 js파일인것으로 보이므로 먼저 index.js를 열어보면

01로 팩맨의 맵이 나타나 있는듯한 것을 보아하니

게임의 전반적인 구조를 이루고있는 코드로 추측됩니다.

 

이 코드를 쭉 읽다보면

20만점을 달성했는지 아닌지에 대한 조건문이 있는 것을 보니

이 분기문의 조건을 만족해야만 플래그를 얻을수 있을것입니다.

 

 

이 부분을 보면, 20만점을 얻었을 때 다음과 같은 GET 요청을 보낸다는 것을 알수 있습니다. 그런데 자세히보면 /validate_Token?token= 뒤에 게임토큰이 있어야 하는것으로 보입니다. 그러므로 게임토큰이라는 것을 어떻게 얻을수 있는것인지 찾아보면

 

 

다음과 같이 /getToken 로 접속하면 토큰을 얻을수 있을 것 같습니다.

 

그러므로 한번 이 주소로 가보면

 

다음과 같이 16진수로 보이는 토큰이 나온다는 것을 알수 있습니다.

그러므로 이 토큰을 아까전에 찾았던 쿼리와 합쳐서 연결해보면

 

플래그를 얻을수 있습니다.

 

 

 

 

 

PROGRAMMING

 

수학교실

먼저 서버에 접속해보면

다음과같이 프로그램이 시작하면서 사칙연산을 기반으로하는 문제들이 출제됩니다.

여러 번 문제를 풀어보면서 사용되는 연산자들을 알아보면

Plus = + , minus = - , multiply = * , floor_division = //

다음과 같이 연산자들을 파악했습니다.

이 문제는 사칙연산 문제를 300번 풀어야하기 때문에

파이썬을 통해서 자동화 스크립트를 짜야합니다.

지금까지 파악한 구조를 기반으로 스크립트를 짜보면 다음과 같습니다.

이렇게 완성한 스크립트를 실행하면

다음과 같이 300번의 연산끝에 성공적으로 플래그를 얻게되었습니다.

 

 

 

 

PWNABLE

 

rpsGameV1.0

 

문제에서 주어진 바이너리 파일을 다운받아서 정보를 확인해보면

32비트 elf파일이고 별다른 보호기법은 없습니다.

이제 한번 실행시켜보면

 

 

 

대략 이러한 구조로 이루어져있습니다.

이제 한번 gdb로 열어보겠습니다.

 

Main 함수를 열어보면 이 부분이 bof 터질것으로 보이는 부분입니다.

Buf 크기가 0x400 이고 32비트 elf 이므로 임의의값으로 0x404(buf+sfp)바이트만큼 덮어씌우면

될것입니다.

PoC를 실행시키면

다음과 같이 성공적으로 플래그를 얻었습니다.

 

 

 

 

 

 

 

 

rpsGameV2.0

먼저 주어진 바이너리의 정보를 확인해보면

 

저번 문제와 같습니다.

한번 실행시켜보면

 

아까전과 기본적인 구조는 같아보입니다.

그럼 어떻게 달라졌는지 gdb를 통해서 분석해보겠습니다.

 

 

 

 

main함수의 어셈블리를 분석해보면 아까와는 조금 달라진것으로 보이는데

먼저 gets 함수로 0x404 만큼의 버퍼에 입력을 받은뒤 strlen함수를통해서

문자열의 길이를 얻어낸뒤에 그 길이를 0x400과 비교한뒤 문자열의 길이가 0x400보다 작거나 같으면 Main+124로 점프하고 아니라면 그 다음 어셈블리로 넘어가게 되는데

어떤 문자열을 출력한뒤 main+260으로 점프합니다.

main+260leave_ret 가 있는 곳이므로 main함수를 종료시키는과 같습니다.

문자열을 확인해보니 bof가 탐지되었다는 문자열이므로

이 부분은 bof를 탐지하는 부분으로 보입니다.

그리고 이 아래에 있는 banner함수는 말그대로 배너를 출력하는 함수이고

나머지 puts, printf 함수를 호출하는 구간도 별다른 취약점은 없으니 넘어가면

이 부분이 보이는데, 먼저 scanf 함수에 취약점이 있을까하고 포맷스트링을 살펴보면

다음과 같아서 이 부분에는 별다른 취약점은 없습니다.

그런데 그 다음부분을 보게되면

Ebp-0x4 의 값과 0x1과 비교를해서 값이 같지않다면 userWin 함수를 호출하고

만약 같다면 userLose 함수를 호출합니다.

이 변수는 처음에 1로 초기화가 되었기 때문에 이 부분에 bof를 성공시켜야만

플래그를 얻을수 있을것입니다.

 

Strlen 함수는 널바이트를 만나면 문자열의 끝이라고 인식하기 때문에

할당된 버퍼를 전부다 널바이트로 덮어씌우면 strlen함수의 검사도 우회할수있고

ebp-0x4 도 널바이트로 덮어씌워질 것 입니다.

 

이를 기반으로 PoC를 짜보면 다음과 같습니다.

PoC를 실행하면

 

플래그를 얻어내게 되었습니다.

 

 

 

 

 

 

 

 

 

 

BASIC LINUX

 

바이너리

 

먼저 ssh로 서버에 접속해줍니다.

그러면 이렇게 로그인이 되면서 문제의 설명이 출력됩니다.

/challone/challone-yoink 라는 elf 파일에서 뭔가를 찾아야합니다.

 

디렉토리로 이동해서 이 elf파일을 살펴보면 아까전에 익스했던 게임이

이상하게 변조되어있습니다.

 

일단 당장 이 서버에서 elf파일의 숨겨진 무언가를 찾으려면 어떻게 해야할까 고민하다가

저는 그냥 xxd를 이용해서 바이너리값을 전부 뽑아냈습니다.

이렇게 해서 출력된 값들을 쭉 살펴보다보니

문자열들이 저장되어있는 영역에서 플래그값을 찾았습니다.

 

 

 

 

 

프로세스

 

Ssh로 서버에 접속해보면

 

아까처럼 로그인이 되면서 문제설명이 출력됩니다.

이번에는 서버에서 돌아가고있는 프로세서를 찾아봐야하는 문제입니다.

저는 ps -ef 명령어를 통해서 모든 프로세스를 출력시켜봤는데

이렇게 대놓고 플래그가 나와있어서

너무나도 쉽게 찾아버렸습니다….

 

 

 

 

숨은 글자 찾기

 

Ssh 로 서버에 연결하면

로그인이 되면서 문제의 설명이 출력되는데

/challthree/challthree-chall.txt 를 찾으면 되는것으로 보입니다.

생각보다 쉽네? 라는 생각을 가지고 텍스트 파일을 출력해봤는데

터미널에 전부다 출력되지 않을만큼 엄청나게 많은 텍스트가 저장되어있습니다.

 

그렇기 때문에 이 텍스트파일이서 플래그를 뽑아낼수있도록 명령어를 사용할것입니다.

플래그에 공통적으로 포함되어있는 문자열은 “segfault{“ “}” 입니다.

 

이것을 기반으로 조건을 맞춰서 플래그를 찾아보면

다음과 같이 플래그가 포함된 줄을 출력해서 플래그를 얻어냈습니다.

 

 

REVERSING

 

터치터치

 

이번 문제는 주어진 apk 파일을 분석해서 플래그를 얻어야하는 문제입니다.

 

먼저 주어진 apk파일의 복사본을 만든뒤 확장자를 zip으로 바꿔줍니다.

그런다음에 이 zip파일의 압축을 해제하면

다음과 같이 구성파일들을 얻을수 있습니다.

여기서 분석을 진행해야하는 파일은 classes.dex 파일인데

이 파일을 제대로 분석하려면 jar 파일로 바꿔줘야 합니다.

 

이것은 dex2jar 이라는 프로그램으로 바꿀수 있습니다.

 

Dex2jar 폴더에 classes.dex를 복사한뒤에

 

cmd창을 켜서 dex2jar 폴더로 이동한다음

d2j-dex2jarclasses.dex파일을 jar파일로 변환해줍니다.

그러면 이렇게 폴더안에 jar파일이 생성됩니다.

이제 본격적인 분석을 진행해야합니다.

 

 

 

먼저 jd-gui로 열어보면

다음과 같이 여러 파일들이 보이는데

실질적으로 분석해야할 부분은 표시된 파트입니다.

 

빠르게 코드들을 살펴보다보면

countActivity.class 에서 손쉽게 플래그를 찾을수 있습니다.

 

 

 

MAKE LUCK

 

먼저 EXEINFOPE로 정보를 확인해보면

GCC로 컴파일된 32비트기반 실행파일입니다.

 

한번 프로그램을 실행시켜보면

숫자를 입력할 수 있고 입력을 해보면 try again next time!! 이라는 문자열이 출력됩니다.

아마 틀려서 그럴것입니다.

 

 

 

 

IDA로 열어보면

 

main함수는 다음과 같은 구조로 이루어져있습니다.

처음부터 분석해보면 input : 이 출력된뒤에 fgets로 입력을 받고

Str에 저장합니다. 그다음에 strlen함수로 문자열의 길이를 알아내서

문자열 끝에있는 문자가 개행문자일경우 그 문자를 0으로 바꿔줍니다.

 

그다음에는 Destination으로 문자열을 복사한뒤에

Fdjidjf 라는 함수에 Destination를 인자로 넣고 호출합니다.

Fdjidjf 함수를 살펴보면 살짝 복잡한 연산이 되고있습니다.

이 함수를 디컴파일해보면

다음과 같은 구조를 하고있습니다.

구조를 분석해보면 초기화되있는 임의의 값을 v2배열에 복사한뒤

배열의 처음부터 끝까지 돌면서 특정연산을 거친후 입력값이 최종연산값과

다르다면 dvckdjq() 함수를 호출한뒤 종료하고 전부다 같다면 zfmiejf() 함수를 리턴합니다.

 

맥락상 따졌을 때 입력값이 각각의 연산값과 한자리라도 틀리면 바로 dvckidjq 함수를 호출하고 종료되기 때문에 자릿값 검사가 이루어진다고 할수있고

zfmioejf함수가 호출된다는 것은 이 함수에서 만들어진 연산값과 입력값이 같아서 호출되었다는것이므로 자릿값 검사를 통과했다는 의미가됩니다.

 

그러므로 연산에 사용되는 값들을 가져오고 연산을 똑같이 해주는 코드를 짜서 올바른 입력값을 알아내야 합니다. 그렇게 해서나온 코드는 다음과 같습니다.

이 연산코드를 실행시키면 다음과 같은 값이 나오는데

이 값을 프로그램에 입력하면

다음과 같이 플래그를 얻게 됩니다.

 

 

 

EVENT

 

Lotto

 

대회를 시작하기전에 첫번째 이벤트로 로또를 추첨하는 이벤트가 있어서 처음에는 이런 오락요소도 있는 대회이구나 라고 생각하고 있었습니다.

 

그런데 대회가 시작하고나서 1시간이 지나니

 

이런 공지가 올라왔습니다. 역시나 단순히 로또를 추첨하는 이벤트가 아니였어서 생각보다 조금 신박했습니다.

11시에 로또추첨을 했기 때문에 1시간안에 프로그램을 분석해야해서 저는 ida의 힘을 조금 빌렸었습니다.

먼저 exeinfope로 정보를 확인해보면

64비트기반 실행파일인것으로 보입니다. Gcc로 컴파일되었으므로 C언어로 작성된 프로그램이라는것을 알수있습니다.

프로그램을 한번 실행해보면

다음과 같이 어드민 비밀번호를 입력할수있고 입력을할 때 리눅스에서 비밀번호를 칠때처럼

비밀번호가 표시되지 않습니다.

이제 한번 ida로 열어보면

 

먼저 프로그램내에서 보이는 문자열 목록을 보면

아까전에 봤었던 문자열들 밑에 로또번호를 출력하는듯한 문자열이 하나 보입니다.

일단 이 문자열이 사용된 함수를 확인해보면 main함수가 나옵니다.

 

 

 

 

 

Main 함수가 나오는데 이 함수를 분석해보면, 7개의 요소를 가진 배열에 요소 하나하나의 값을 복잡한 연산을 한뒤에 저장을 한뒤 그 배열을 로또번호라는 문자열 다음에 하나하나씩 출력을 하는것으로 봐서 이 배열에 저장된 최종연산값이 바로 로또번호일것입니다.

 

main함수에서 로또번호를 바로 출력하는것으로 보이는데 이전에 비밀번호를 검사하는 함수는 어디있는건지 찾아봐도 main함수내에서 사용자정의함수를 호출한 부분이 보이지 않습니다.

그러므로 main함수를 호출한 함수는 어디인지 한번 살펴보면

이렇게 main함수를 호출한 또다른 main함수가 보입니다.

분명 main함수인데도 또다른 main함수에서 호출을 한것으로 보입니다.

 

그래서 뭔가싶어서 함수목록을 자세히 찾아보면

다음과 같이 main함수와 아까 제가 분석했던 _main함수가 다른 함수다라는 것을 알수 있습니다.

_main함수를 호출한 main함수를 분석해봐야할것입니다.

이 함수를 간단하게 분석해보면,

banner함수를 호출해서 배너를 출력한뒤, scanf함수로 입력을 받은뒤 sprintf함수로 buffer에 복사합니다. 그리고나서 str2에 어떤 문자열을 복사한뒤 입력받은 문자열의 길이를 변수에 저장하고 buffer buffer의 길이를 base64_encode함수의 인자로 전달한뒤 호출합니다.

함수를 분석해보니 이름과 같이 문자열을 base64방식으로 인코딩하는 함수입니다.

그런 다음에 str2와 인코딩된 입력 문자열을 비교해서 서로 다르다면 Passwords do not match. 라는 문자열을 출력한뒤에 get_ch 함수를 호출하고 같으면 아까전에 분석했던 _main함수를 호출합니다.

 

get_ch함수를 살펴보면

 

 

 

키 입력을 받으면 프로그램을 종료하는 함수입니다.

 

지금까지의 분석을 기반으로 프로그램의 구조를 생각해보면

입력값의 base64 인코딩값과 특정 문자열을 비교해서 같으면 로또번호를 출력해주는 시스템입니다.

그러므로 입력해야할 번호가 무엇인지 알아내야 합니다.

Base64 인코딩은 쉽게 디코딩할수있습니다. 그러므로 한번 이 문자열을 디코딩해보면

STJDc2QjOURAOHc3RU53bHR6RGd4TyYw -> I2Csd#9D@8w7ENwltzDgxO&0

다음과 같이 디코딩이 되었습니다. 이 문자열이 바로 어드민 패스워드인것입니다.

 

그럼 이제 이 문자열을 입력하면 로또번호가 출력이 되야하는건데 문제는 입력을 하면 프로그램이 바로 종료됩니다.

아마도 로또번호를 출력한뒤에 잠시 멈추는 기능이 없기 때문에 그런것이라고 생각이 됩니다.

그러므로 로또번호를 출력하는 라인에 중단점을 설정해서 출력되는 번호를 확인해보면

 

다음과 같이 로또번호를 얻어냈습니다.

그런데….제가 원래 얻었던 로또번호와 다릅니다….?

그래서 이게 왜 이런건지 싶어서 로또번호를 생성하는 부분을 다시 살펴보면

 

다음과 같이 로또번호를 생성하는 로직을 살펴보면 프로그램이 실행되는 당시의

,,일을 수집한뒤에 그 값을 기반으로 로또번호를 생성하는 프로그램입니다.

지금 write-up을 쓰고있는 날짜는 2022-07-31 이고 이 문제를 풀었던 당시의 날짜는

2022-07-30 이므로 하루가 지난 상태에서 다시 로또번호를 생성해서 이런것이라고 판단할수

있습니다. 실제로 동적분석을 진행해서 받은 날짜값을 살펴보면

0x1F = 31, 역시나 저의 예상과같이 31이라는 값이 저장되있다는 것을 알수있습니다.

날짜마다 새로운 로또번호를 생성하는 시스템으로 보이므로

날짜를 0x1E = 30 으로 바꾼뒤 로또번호를 생성하고 출력시켜보면

2022-07-30 일때 생성된 로또번호를 얻을수 있습니다.

 

 

 

공지된 추첨결과를 확인해보면

정확히 같으므로 올바른 로또번호라는 것을 알수 있습니다.

 

이번 CTF는 제가 처음 뛰어보는 CTF였는데 생각보다 문제들의 난이도나 퀄리티가 높았고

다른 CTF에서는 느껴보기 힘든 스토리도 좋았던 것 같습니다.

CTF이다보니 부족한점도 많았지만 좋은 경험이 됐습니다.

 

728x90

'CTF' 카테고리의 다른 글

2023 TAMUctf write-up  (0) 2023.05.15
[2023 root access CTF] write-up  (3) 2023.05.02
[2023 FooBar CTF] Write-up  (0) 2023.03.14
[Reversing] Baby_Keylogger  (0) 2022.07.09
[Forensic] Baby_RSA writeup  (0) 2022.05.11