Skip to content

Instantly share code, notes, and snippets.

@matthw
Last active February 13, 2023 08:18
Show Gist options
  • Save matthw/f93b883b2484a10d09d732f057cd4613 to your computer and use it in GitHub Desktop.
Save matthw/f93b883b2484a10d09d732f057cd4613 to your computer and use it in GitHub Desktop.

LACTF 2023

1 rev/universal

Open class file in jadx

package p000;

import java.nio.charset.Charset;
import java.util.Scanner;

/* renamed from: FlagChecker */
/* loaded from: FlagChecker.class */
class FlagChecker {
    FlagChecker() {
    }

    public static void main(String[] strArr) {
        System.out.print("What's the flag? ");
        System.out.flush();
        Scanner scanner = new Scanner(System.in);
        String nextLine = scanner.nextLine();
        scanner.close();
        byte[] bytes = nextLine.getBytes(Charset.forName("UTF-8"));
        if (bytes.length == 38 && (((bytes[34] ^ (bytes[23] * 7)) ^ ((bytes[36] ^ (-1)) + 13)) & 255) == 182 && (((bytes[37] ^ (bytes[10] * 7)) ^ ((bytes[21] ^ (-1)) + 13)) & 255) == 223 && (((bytes[24] ^ (bytes[23] * 7)) ^ ((bytes[19] ^ (-1)) + 13)) & 255) == 205 && (((bytes[25] ^ (bytes[13] * 7)) ^ ((bytes[23] ^ (-1)) + 13)) & 255) == 144 && (((bytes[6] ^ (bytes[27] * 7)) ^ ((bytes[25] ^ (-1)) + 13)) & 255) == 138 && (((bytes[4] ^ (bytes[32] * 7)) ^ ((bytes[22] ^ (-1)) + 13)) & 255) == 227 && (((bytes[25] ^ (bytes[19] * 7)) ^ ((bytes[1] ^ (-1)) + 13)) & 255) == 107 && (((bytes[22] ^ (bytes[7] * 7)) ^ ((bytes[29] ^ (-1)) + 13)) & 255) == 85 && (((bytes[15] ^ (bytes[10] * 7)) ^ ((bytes[20] ^ (-1)) + 13)) & 255) == 188 && (((bytes[29] ^ (bytes[16] * 7)) ^ ((bytes[12] ^ (-1)) + 13)) & 255) == 88 && (((bytes[35] ^ (bytes[4] * 7)) ^ ((bytes[33] ^ (-1)) + 13)) & 255) == 84 && (((bytes[36] ^ (bytes[2] * 7)) ^ ((bytes[4] ^ (-1)) + 13)) & 255) == 103 && (((bytes[26] ^ (bytes[3] * 7)) ^ ((bytes[1] ^ (-1)) + 13)) & 255) == 216 && (((bytes[12] ^ (bytes[6] * 7)) ^ ((bytes[18] ^ (-1)) + 13)) & 255) == 165 && (((bytes[12] ^ (bytes[28] * 7)) ^ ((bytes[36] ^ (-1)) + 13)) & 255) == 151 && (((bytes[20] ^ (bytes[0] * 7)) ^ ((bytes[21] ^ (-1)) + 13)) & 255) == 101 && (((bytes[27] ^ (bytes[36] * 7)) ^ ((bytes[14] ^ (-1)) + 13)) & 255) == 248 && (((bytes[35] ^ (bytes[2] * 7)) ^ ((bytes[19] ^ (-1)) + 13)) & 255) == 44 && (((bytes[13] ^ (bytes[11] * 7)) ^ ((bytes[33] ^ (-1)) + 13)) & 255) == 242 && (((bytes[33] ^ (bytes[11] * 7)) ^ ((bytes[3] ^ (-1)) + 13)) & 255) == 235 && (((bytes[31] ^ (bytes[37] * 7)) ^ ((bytes[29] ^ (-1)) + 13)) & 255) == 248 && (((bytes[1] ^ (bytes[33] * 7)) ^ ((bytes[31] ^ (-1)) + 13)) & 255) == 33 && (((bytes[34] ^ (bytes[22] * 7)) ^ ((bytes[35] ^ (-1)) + 13)) & 255) == 84 && (((bytes[36] ^ (bytes[16] * 7)) ^ ((bytes[4] ^ (-1)) + 13)) & 255) == 75 && (((bytes[8] ^ (bytes[3] * 7)) ^ ((bytes[10] ^ (-1)) + 13)) & 255) == 214 && (((bytes[20] ^ (bytes[5] * 7)) ^ ((bytes[12] ^ (-1)) + 13)) & 255) == 193 && (((bytes[28] ^ (bytes[34] * 7)) ^ ((bytes[16] ^ (-1)) + 13)) & 255) == 210 && (((bytes[3] ^ (bytes[35] * 7)) ^ ((bytes[9] ^ (-1)) + 13)) & 255) == 205 && (((bytes[27] ^ (bytes[22] * 7)) ^ ((bytes[2] ^ (-1)) + 13)) & 255) == 46 && (((bytes[27] ^ (bytes[18] * 7)) ^ ((bytes[9] ^ (-1)) + 13)) & 255) == 54 && (((bytes[3] ^ (bytes[29] * 7)) ^ ((bytes[22] ^ (-1)) + 13)) & 255) == 32 && (((bytes[24] ^ (bytes[4] * 7)) ^ ((bytes[13] ^ (-1)) + 13)) & 255) == 99 && (((bytes[22] ^ (bytes[16] * 7)) ^ ((bytes[13] ^ (-1)) + 13)) & 255) == 108 && (((bytes[12] ^ (bytes[8] * 7)) ^ ((bytes[30] ^ (-1)) + 13)) & 255) == 117 && (((bytes[25] ^ (bytes[27] * 7)) ^ ((bytes[35] ^ (-1)) + 13)) & 255) == 146 && (((bytes[16] ^ (bytes[10] * 7)) ^ ((bytes[14] ^ (-1)) + 13)) & 255) == 250 && (((bytes[21] ^ (bytes[25] * 7)) ^ ((bytes[12] ^ (-1)) + 13)) & 255) == 195 && (((bytes[26] ^ (bytes[10] * 7)) ^ ((bytes[30] ^ (-1)) + 13)) & 255) == 203 && (((bytes[20] ^ (bytes[2] * 7)) ^ ((bytes[1] ^ (-1)) + 13)) & 255) == 47 && (((bytes[34] ^ (bytes[12] * 7)) ^ ((bytes[27] ^ (-1)) + 13)) & 255) == 121 && (((bytes[19] ^ (bytes[34] * 7)) ^ ((bytes[20] ^ (-1)) + 13)) & 255) == 246 && (((bytes[25] ^ (bytes[22] * 7)) ^ ((bytes[14] ^ (-1)) + 13)) & 255) == 61 && (((bytes[19] ^ (bytes[28] * 7)) ^ ((bytes[37] ^ (-1)) + 13)) & 255) == 189 && (((bytes[24] ^ (bytes[9] * 7)) ^ ((bytes[17] ^ (-1)) + 13)) & 255) == 185) {
            System.out.println("Correct!");
        } else {
            System.out.println("Not quite...");
        }
    }
}

shove conditions in z3 (find replace ftw):

from z3 import *

flag = []
for x in range(38):
    c = BitVec('x%d'%x, 8)
    flag.append(c)


s = Solver()

s.add((((flag[34] ^ (flag[23] * 7)) ^ ((flag[36] ^ (-1)) + 13)) & 255) == 182)
s.add((((flag[37] ^ (flag[10] * 7)) ^ ((flag[21] ^ (-1)) + 13)) & 255) == 223)
s.add((((flag[24] ^ (flag[23] * 7)) ^ ((flag[19] ^ (-1)) + 13)) & 255) == 205)
s.add((((flag[25] ^ (flag[13] * 7)) ^ ((flag[23] ^ (-1)) + 13)) & 255) == 144)
s.add((((flag[6] ^ (flag[27] * 7)) ^ ((flag[25] ^ (-1)) + 13)) & 255) == 138)
s.add((((flag[4] ^ (flag[32] * 7)) ^ ((flag[22] ^ (-1)) + 13)) & 255) == 227)
s.add((((flag[25] ^ (flag[19] * 7)) ^ ((flag[1] ^ (-1)) + 13)) & 255) == 107)
s.add((((flag[22] ^ (flag[7] * 7)) ^ ((flag[29] ^ (-1)) + 13)) & 255) == 85)
s.add((((flag[15] ^ (flag[10] * 7)) ^ ((flag[20] ^ (-1)) + 13)) & 255) == 188)
s.add((((flag[29] ^ (flag[16] * 7)) ^ ((flag[12] ^ (-1)) + 13)) & 255) == 88)
s.add((((flag[35] ^ (flag[4] * 7)) ^ ((flag[33] ^ (-1)) + 13)) & 255) == 84)
s.add((((flag[36] ^ (flag[2] * 7)) ^ ((flag[4] ^ (-1)) + 13)) & 255) == 103)
s.add((((flag[26] ^ (flag[3] * 7)) ^ ((flag[1] ^ (-1)) + 13)) & 255) == 216)
s.add((((flag[12] ^ (flag[6] * 7)) ^ ((flag[18] ^ (-1)) + 13)) & 255) == 165)
s.add((((flag[12] ^ (flag[28] * 7)) ^ ((flag[36] ^ (-1)) + 13)) & 255) == 151)
s.add((((flag[20] ^ (flag[0] * 7)) ^ ((flag[21] ^ (-1)) + 13)) & 255) == 101)
s.add((((flag[27] ^ (flag[36] * 7)) ^ ((flag[14] ^ (-1)) + 13)) & 255) == 248)
s.add((((flag[35] ^ (flag[2] * 7)) ^ ((flag[19] ^ (-1)) + 13)) & 255) == 44)
s.add((((flag[13] ^ (flag[11] * 7)) ^ ((flag[33] ^ (-1)) + 13)) & 255) == 242)
s.add((((flag[33] ^ (flag[11] * 7)) ^ ((flag[3] ^ (-1)) + 13)) & 255) == 235)
s.add((((flag[31] ^ (flag[37] * 7)) ^ ((flag[29] ^ (-1)) + 13)) & 255) == 248)
s.add((((flag[1] ^ (flag[33] * 7)) ^ ((flag[31] ^ (-1)) + 13)) & 255) == 33)
s.add((((flag[34] ^ (flag[22] * 7)) ^ ((flag[35] ^ (-1)) + 13)) & 255) == 84)
s.add((((flag[36] ^ (flag[16] * 7)) ^ ((flag[4] ^ (-1)) + 13)) & 255) == 75)
s.add((((flag[8] ^ (flag[3] * 7)) ^ ((flag[10] ^ (-1)) + 13)) & 255) == 214)
s.add((((flag[20] ^ (flag[5] * 7)) ^ ((flag[12] ^ (-1)) + 13)) & 255) == 193)
s.add((((flag[28] ^ (flag[34] * 7)) ^ ((flag[16] ^ (-1)) + 13)) & 255) == 210)
s.add((((flag[3] ^ (flag[35] * 7)) ^ ((flag[9] ^ (-1)) + 13)) & 255) == 205)
s.add((((flag[27] ^ (flag[22] * 7)) ^ ((flag[2] ^ (-1)) + 13)) & 255) == 46)
s.add((((flag[27] ^ (flag[18] * 7)) ^ ((flag[9] ^ (-1)) + 13)) & 255) == 54)
s.add((((flag[3] ^ (flag[29] * 7)) ^ ((flag[22] ^ (-1)) + 13)) & 255) == 32)
s.add((((flag[24] ^ (flag[4] * 7)) ^ ((flag[13] ^ (-1)) + 13)) & 255) == 99)
s.add((((flag[22] ^ (flag[16] * 7)) ^ ((flag[13] ^ (-1)) + 13)) & 255) == 108)
s.add((((flag[12] ^ (flag[8] * 7)) ^ ((flag[30] ^ (-1)) + 13)) & 255) == 117)
s.add((((flag[25] ^ (flag[27] * 7)) ^ ((flag[35] ^ (-1)) + 13)) & 255) == 146)
s.add((((flag[16] ^ (flag[10] * 7)) ^ ((flag[14] ^ (-1)) + 13)) & 255) == 250)
s.add((((flag[21] ^ (flag[25] * 7)) ^ ((flag[12] ^ (-1)) + 13)) & 255) == 195)
s.add((((flag[26] ^ (flag[10] * 7)) ^ ((flag[30] ^ (-1)) + 13)) & 255) == 203)
s.add((((flag[20] ^ (flag[2] * 7)) ^ ((flag[1] ^ (-1)) + 13)) & 255) == 47)
s.add((((flag[34] ^ (flag[12] * 7)) ^ ((flag[27] ^ (-1)) + 13)) & 255) == 121)
s.add((((flag[19] ^ (flag[34] * 7)) ^ ((flag[20] ^ (-1)) + 13)) & 255) == 246)
s.add((((flag[25] ^ (flag[22] * 7)) ^ ((flag[14] ^ (-1)) + 13)) & 255) == 61)
s.add((((flag[19] ^ (flag[28] * 7)) ^ ((flag[37] ^ (-1)) + 13)) & 255) == 189)
s.add((((flag[24] ^ (flag[9] * 7)) ^ ((flag[17] ^ (-1)) + 13)) & 255) == 185)


s.check()

model = s.model()
out = ''

for x in flag:
    out += chr(model[x].as_long())

print(out)

profit:

% python dd.py 
lactf{1_d0nt_see_3_b1ll10n_s0lv3s_y3t}

2 rev/ctfd_plus

entry point looks like:

undefined8 entry(void)

{
    char correct_flag_char;
    size_t sVar1;
    long n;
    undefined4 *puVar2;
    char input_flag [256];
    
    puts("Welcome to CTFd+!");
    puts("So far, we only have one challenge, which is one more than the number of databases we have.\n");
    puts("Very Doable Pwn - 500 points, 0 solves");
    puts("Can you help me pwn this program?");
    puts("#include <stdio.h>\nint main(void) {\n    puts(\"Bye!\");\n    return 0;\n}\n");
    puts("Enter the flag:");
    fgets(input_flag,0x100,stdin);
    sVar1 = strcspn(input_flag,"\n");
    n = 0;
    puVar2 = &DAT_00104060;
    input_flag[sVar1] = '\0';
    do {
        correct_flag_char = whatever(puVar2[n]);
        if (correct_flag_char != input_flag[n]) {
            puts("Incorrect flag.");
            return 0;
        }
        n = n + 1;
    } while (n != 0x2f);
    puts("You got the flag! Unfortunately we don\'t exactly have a database to store the solve in...");
    return 0;
}

we can patch the binary so this is never taken:

        if (correct_flag_char != input_flag[n]) {
            puts("Incorrect flag.");
            return 0;
        }

by changing JZ to JMP:

      00101106 e8 25 01 00 00           CALL                 whatever                                                                                 undefined whatever()
      0010110b 3a 04 33                 CMP                  correct_flag_char,byte ptr [RBX + n*0x1]=>input_flag
HERE  0010110e 74 e8                    JZ                   LAB_001010f8 
      00101110 48 8d 3d 11 0f 00 00     LEA                  RDI,[s_Incorrect_flag._00102028]                                                         = "Incorrect flag."
      00101117 e8 14 ff ff ff           CALL                 <EXTERNAL>::puts                                                                         int puts(char * __s)
data = bytearray(open("ctfd_plus", "rb").read())
data[0x110e] = 0xeb # jz -> jmp
open("ctfd_patched", "wb").write(data)

prepare gbd script to dump AL register after the whatever function call (AL will contains the correct flag chr):

import gdb

def read_reg(reg):
    return gdb.parse_and_eval("${}".format(reg))

def gdb_continue():
    gdb.execute('continue')

gdb.execute('break *0x000055555555510b')   # fix addr
flag = ''
while 1:
    gdb.execute("continue")
    bla = int(read_reg('al'))
    flag += chr(bla)
    print(flag)

then run it on patched binary:

% gdb ./ctfd_patched 
pwndbg> starti
pwndbg> source gggdb.py
Breakpoint 1 at 0x55555555510b
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Welcome to CTFd+!
So far, we only have one challenge, which is one more than the number of databases we have.

Very Doable Pwn - 500 points, 0 solves
Can you help me pwn this program?
#include <stdio.h>
int main(void) {
    puts("Bye!");
    return 0;
}

Enter the flag:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x000055555555510b in ?? ()
l

Breakpoint 1, 0x000055555555510b in ?? ()
la

Breakpoint 1, 0x000055555555510b in ?? ()
lac
///
Breakpoint 1, 0x000055555555510b in ?? ()
lactf{m4yb3_th3r3_1s_s0m3_m3r1t_t0_us1ng_4_d

Breakpoint 1, 0x000055555555510b in ?? ()
lactf{m4yb3_th3r3_1s_s0m3_m3r1t_t0_us1ng_4_db

Breakpoint 1, 0x000055555555510b in ?? ()
lactf{m4yb3_th3r3_1s_s0m3_m3r1t_t0_us1ng_4_db}

3 rev/switcheroo

00401000  int64_t _start() __noreturn

00401017      syscall(sys_write {1}, fd: 1, buf: "Give me the flag: That was not t…", count: 0x12)
00401025      int64_t rax = syscall(sys_read {0}, fd: 0, buf: &__return_addr, count: 0x64)
0040102b      if (rax == 0x40)
0040102d          int64_t r12_1 = 0
0040103e          for (int64_t r10_1 = 0; r10_1 s< rax; r10_1 = r10_1 + 1)
0040104d              int64_t r8_2 = rol.q(*((zx.q(*(&__return_addr + r10_1)) << 3) + 0x40203c), 8)
00401051              uint64_t r13_1 = zx.q(r8_2.b)
00401055              int64_t r14_1 = 0
0040105b              while (true)
0040105b                  if (r14_1 s>= r13_1)
0040106b                      r12_1 = r12_1 + 1
0040106b                      break
0040105d                  r8_2 = rol.q(r8_2, 8)
00401064                  if (r8_2.b == r10_1.b)
00401064                      break
00401066                  r14_1 = r14_1 + 1
00401076          if (r12_1 == 0)
00401089              return sub_40109a(0, &data_40202a, 0x12) __tailcall   // win
00401098      return sub_40109a(0, "That was not the flag :(That was…", 0x18) __tailcall

input string of len 0x3f (0x40 with \n), then each char is checked i dont care/know how but:

  • every incorrect flag char increment r12 register
  • at the end, if r12 == 0, it's a win

so we can bruteforce each char using a gdb script:

import gdb
import string

passlen = 0x40
break_addr = 0x00401073

code = ["_"]*passlen
printable = string.printable[:-5]


def gdb_run_with_stdin(pwd):
    with open('_cracking', 'w') as f:
        f.write(pwd)

    gdb.execute('run < _cracking')

def read_reg(reg):
    return gdb.parse_and_eval("${}".format(reg))

def gdb_continue():
    gdb.execute('continue')


gdb.execute('break *{}'.format(break_addr))


errors = -1
for trial in range(passlen):
    for c in range(0x21, 0x7E+1):
        code[trial] = chr(c)

        print("trying: %s"%''.join(code))

        gdb_run_with_stdin(''.join(code))
        res = int(read_reg("r12"))
        if errors == -1:
            errors = res
            continue

        if res > errors:
            errors = res

        if res < errors:
            print('FOUND: %s'%''.join(code))
            errors = res
            break

print(''.join(code))
% gdb switcheroo 
pwndbg>  source lolz.py
//// wait for it
trying: lactf{4223M8LY_5W17Ch_57473M3n75_4r3_7h3_4850LU73_8357_u+1f60az_
Give me the flag: 
Breakpoint 1, 0x0000000000401073 in ?? ()
trying: lactf{4223M8LY_5W17Ch_57473M3n75_4r3_7h3_4850LU73_8357_u+1f60a{_
Give me the flag: 
Breakpoint 1, 0x0000000000401073 in ?? ()
trying: lactf{4223M8LY_5W17Ch_57473M3n75_4r3_7h3_4850LU73_8357_u+1f60a|_
Give me the flag: 
Breakpoint 1, 0x0000000000401073 in ?? ()
trying: lactf{4223M8LY_5W17Ch_57473M3n75_4r3_7h3_4850LU73_8357_u+1f60a}
Give me the flag: 
Breakpoint 1, 0x0000000000401073 in ?? ()
FOUND: lactf{4223M8LY_5W17Ch_57473M3n75_4r3_7h3_4850LU73_8357_u+1f60a}_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment