some of Reversing chall
1. BackTo1986 - Reversing ( 839 p )
커널 파일이 주어진다.
gunzip initramfs.cpio.gz
mkdir tmp && cd tmp
cpio -idmv < ../initramfs.cpio
main 바이너리가 주어진다.
qemu로 start.sh 명령을 실행시키면 다음 화면이 뜬다.
qemu-system-x86_64 -kernel ./bzImage -initrd ./initramfs.cpio.gz -vga std --append "console=ttyS0 console=tty0"
블록을 깨는 핑퐁게임임을 알 수 있다.
바이너리를 분석해보자.
문자열로 유추할 수 있는 함수부터 살펴보면 UI를 그려주는 함수임을 알 수 있다.
이 함수에 DrawUI 이름을 붙여주고 이 함수를 호출하는 함수 위로 올라가보면
tfblib 관련 문자열을 볼 수 있다.
https://github.com/vvaltchev/tfblib
라이브러리를 사용함을 알 수 있고, https://vvaltchev.github.io/tfblib/tfblib_8h.html 문서를 통해 DrawUI 속 함수들의 이름을 유추할 수 있다. 인자 개수와 마지막에 색이 들어가는 부분까지 알 수 있고, 덤으로 ball과 stick 의 좌표가 x y 4바이트씩 이루어져있다는 것도 알 수 있다. Point 구조체 만들어주고 각 네이밍까지 다 해준다.
DrawUI를 호출하는 함수에 게임의 메인 루프가 있다.
stage 가 증가할때마다 각 좌표를 다시 설정해주고 /dev/prob 파일을 읽어 블록 설정을 한다.
공을 움직이는 함수 내에서 /dev/prob에 값을 쓰는 부분이 있는데, 생성된 블록을 없앨 때마다 순서대로 /dev/prob에 값이 써지는 것을 알 수 있다. 매 판마다 공이 생성되는 위치, 속력 등이 모두 일정하기 때문에 깨는 블록 순서에 따라 다음 블록의 모양이 결정되고, 각 블록의 모양이 플래그의 각 글자임을 유추할 수 있다.
원활한 플래그 확인을 위해 다음 패치를 진행했다.
1. 공의 속력 증가
2. 막대기 판정 크기 변경 및 위치 고정
3. 각 블록의 표시 사이즈 정사각형으로 변경 ( 40 x 20 -> 20 x 20 )
먼저 공의 속력은
로 (1, 1) 인데, 이를 (5, 5) 로 수정하였다.
막대기에 충돌하여 반사되는 부분의 조건을 수정하여 공이 y좌표만 같아지면 반사되게끔 하였다.
stick의 좌표를 (0, 0x28) 로 고정하였다.
패치한 main 을 가지고 다시 cpio로 압축한다.
cd tmp
cp ../main ./main
find . | cpio -ov --format=newc > ../initramfs2.cpio
cd ..
gzip initramfs2.cpio
이를 다시 qemu로 실행시키면
각 블록이 정사각형이 되어 무슨 알파벳인지 식별하기 쉬워졌고, 막대는 왼쪽에서 생성되며 실행해보면 가만히 냅둬도 공이 밑에서 반사된다. (보이지 않는 벽이 있는 것처럼) 블록은 Draw 만 작게 한거라서 실제 충돌되는 판정은 기존과 같다.
이후 가만히 냅두어 다음 스테이지로 넘어갈 때까지 기다린다.
1분 안으로 다음 단계로 넘어갈 수 있다. 넘어갈 때마다 수작업으로 알파벳 기록하고 키 눌러서 실행해주고를 반복한다.
길어도 몇십글자로 끝날 것 같아 더 빠르고 자동화된 솔버를 만들지 않다가, 100글자 넘어갈 때부터 쎄해서 급하게 이를 똑같이 구현하는 코드를 짜다가 결국 255글자까지 가자 닫는 중괄호가 나와 플래그를 모두 얻을 수 있었다.
새벽 4시부터 돌려서 오전 9시 반에 닫는 중괄호가 나왔으니 거의 5시간이 걸렸다.
FLAG :
codegate2023{JBALFNZW8SGBH0LJT1NFYKRKV8RMLWXA9FMYMO3PUJ18TYFGSFWDTNK30JCH8PBX127RR75UFQI6649K4H7NF00ZAC0WG5Y3V9I3HH1AEWXS4PGFAH8K9FHVPK02MW0LKCP08P3CXXA7B5OMORJP8N9XYXWIS8WUMNXDRHC6IIOVSMYPT51FMXHEIUVZ4BM5WF6W960IXBVQ3MZZCUJLR330QMPJ5UGADZNC8E48EJU0DCWZN}
codegate2023{JBALFNZW8SGBH0LJT1NFYKRKV8RMLWXA9FMYMO3PUJ18TYFGSFWDTNK30JCH8PBX127RR75UFQI6649K4H7NF00ZAC0WG5Y3V9I3HH1AEWXS4PGFAH8K9FHVPK02MW0LKCP08P3CXXA7B5OMORJP8N9XYXWIS8WUMNXDRHC6IIOVSMYPT51FMXHEIUVZ4BM5WF6W960IXBVQ3MZZCUJLR330QMPJ5UGADZNC8E48EJU0DCWZN}
2. mipgoRPG - Reversing
go 언어로 만들어진 서버가 있고, 웹소켓으로 데이터를 주고받으며 게임이 진행된다.
이런 식으로 스테이지를 클리어해나가면 된다.
바이너리도 주어져서 이를 분석해볼 수 있다.
main_main
main_main 에서 InitClass 를 호출하고, 서버를 연다.
InitClass
InitClass에서는 각 클래스(Warrior, Wizard, Theif), 스킬들, 몬스터 들의 정보를 각 배열에 넣어둔다.
구조체 심볼이 있어서 타입을 적절하게 바꿔주면 예쁘게 볼 수 있다.
각 몬스터는 아이템을 들고 있을 수 있고, 처치할 시 플레이어가 가지게 된다.
클리어
워리어로 플레이를 해보면 스테이지 4쯤에서 체력이 동나서 깰 수 없는 구조로 되어있다.
보면 마지막 Enemy 는 체력이 9999고 공격력은 100인데, 정상적인 방법으로는 깰수 없어보인다.
메인 루틴
Run()
{
turn = 0
for level <= enemyCnt and hp > 0:
turn += PlayGame()
level++
if hp > 0:
# print tmeplate
if turn < 10:
# print FLAG
}
PlayGame()
{
turn = 1
while(1)
{
if player.hp <= 0 or enemy.hp <= 0:
break
# Fight
turn++
}
return turn
}
간단하게 주요 로직을 표현해보면 위와 같다.
목표
플래그를 얻기 위해서는 10 턴 이내로 모든 스테이지를 클리어해야한다.
분석
일단 턴 수가 어찌되었든 클리어를 해야하기 때문에, PlayGame 안을 자세히 분석해봐야 한다.
물론 분석 없이 플레이해보면서 알 수 있겠지만, 바이너리를 준 이유와 코드가 워낙 길어 어떤 로직 버그가 있을 수 있기 때문에 분석한다.
플레이어가 데미지를 입는 부분을 보면 enemy_damage 에서 - dp를 빼준 값을 빼는데, 이 때 dp 가 dmg 보다 더 크면 체력이 오히려 차는 버그를 생각해 볼 수 있다.
defense 를 선택하면 플레이어의 최소 방어력만큼 dp를 설정해준다.
워리어의 두 번째 스킬을 보면 체력 150을 깎고 방어력을 10 올린다.
클리어
ㅋㅋ 마지막 몬스터 체력을 몰랐을 때 혹시를 대비해 체력을 10만까지 키웠다.
130번 정도 1번 스킬 난사하면 죽는다.'
이렇게 html 이 불러와지는데, 이 부분 코드를 보자
클리어 시 루틴
템플릿에 사용자이름을 넣는다.
턴 수가 10 이내이면 플래그를 읽어서 덧붙여주는데, 여기에 속아서 진전이 없었다.
1. 숨겨진 직업, 스킬, 아이템 이 있는가?
- main_classes, main_Skills, main_enemies 에 write 하는 부분은 InitClass 밖에 없고, 딱 정해진 양만큼만 있음.
2. PlayGame이 리턴하는 turn을 조작할 수 있는가?
- 없음
3. Run 의 turn 을 조작할 수 있는가?
- 없음
4. Run 의 level 을 조작할 수 있는가?
- 없음
5. level 이 enemyCnt인 7이 될 때까지 클리어할 수 있는가?
- Warrior 선택하여 defense 뻥튀기하면 가능, 대략 200턴 정도 소요. 그 Wizard 와 Thief 로는 불가능
6. 5. 를 10 턴 이내로 할 수 있는가?
- 정상적인 결투라면 불가능. 원콤 낼 공격력과 한 대씩 맞고 안 죽을 체력 필요
7. 원콤낼 공격력을 가질 수 있는가?
- 숨겨진 직업, 스킬, 아이템이나 비트 플립이 일어나거나 메모리 버그가 나거나 로지컬 버그 발생해야함
위처럼 정리하고 코드를 열심히 뒤집어봐도 로직 버그가 일어나는 부분이 없어서 포기했다.
실제 풀이
대회 끝난 후 다른 분(저라보)님께서 SSTI 로 풀었다고 말씀해주셨다.
ㅋㅋㅋㅋ 거의 24시간을 리버싱만 보고 있었는데 ssti 생각은 정말 하지도 못했다. 대놓고 템플릿이랑 이름을 주는데 이에 대한 고민은 위 노트에 포함되어있지 않다. 웹 분야로 나왔으면 SSTI를 바로 봤을 텐데 참 아이러니하다. 간판만 다르게 갈아끼웠다고 보는 시각이 아예 달라져서 쉬운걸 못보니..
3. Dirty Dart - Reversing
Dart로 컴파일한 바이너리가 주어진다.
찾아보니 Dart는 Dart VM 을 사용하여 AOT 방식으로 실행한다는데, exe 리버싱에 대한 정보가 없어서 뭐 해보지도 못한 문제이다. 끝나고 매티님께 물어보니 ELF 가 snapshot 섹션에 있다고 하셨다. ELF 만 뽑아내면 별 어려움 없이 풀 수 있는 문제로 보인다.
뽑아서 아이다로 보면
main 이 있따.
a 는
와 같이 생겻다.
반복되는 if 문이 쭉 있는 걸 보고 한 글자씩 검사한다고 생각해볼 수 있다.
한 글자씩 검사하는, 즉 각 바이트 간이 독립적으로 연산되면 브포로 해결이 가능하다.
https://velog.io/@delom745/Codegate-2023-Preliminary-Dirty-Dart
매티님은 틀렸을 때 Wrong 으로 가는게 아니라 맞았을 때 가도록 패치하고 브포를 돌리셨다.
'CTF Writeup' 카테고리의 다른 글
zer0ptsCTF 2023 - mimikyu Writeup (0) | 2023.07.19 |
---|---|
zer0ptsCTF 2023 - fvm Writeup (1) | 2023.07.18 |
Defcon 2023 Qualifier - kkkkklik writeup (0) | 2023.05.31 |
2022 Incognito CTF Writeup (0) | 2023.03.26 |
2023 ACSC CTF Writeup - warmup + ngo (0) | 2023.02.26 |