Skip to content

Instantly share code, notes, and snippets.

@Stfort52
Last active October 24, 2023 08:15
Show Gist options
  • Save Stfort52/b6fa82805ed44404e38f560b40d07e40 to your computer and use it in GitHub Desktop.
Save Stfort52/b6fa82805ed44404e38f560b40d07e40 to your computer and use it in GitHub Desktop.
Write-up for CODEGATE 2022 Finals reversing challenge verylog

CODEGATE 2022 Finals verylog

TL;DR

Analysis

These modules,

  1. generate six permutation states from fixed seed (T_0)
  2. opens key.txt(T_2)
  3. reads each numbers from key.txt(T_3)
  4. generates answer by permutation (T_1)
  5. compares answer with input (T_3)
  6. checks if you got exactly 24 correct answers (T_3)
  7. prints 'Success Auth!' if you got it right. (T_7)

Solution

There are only 32 answers, and we can try 1023 times. Brute force to find a answer and spam it to get 24 matches.

790 1231s will do the trick.


Analysis

We're given a ICARUS Verilog assembly file app, which can be directly executed under vvp simulation engine. Along with that, we have a simple wrapper script written in python. This python script reads in up to 1023 numbers, and feeds them into the app via a temp file. If the app exits with Success Auth!, it just prints out the flag.

To solve this problem, we need to analyze the app and find a way to make it print out Success Auth!. Unlike the verilog source code itself, the compiled assembly was not very human friendly. The vvp instruction set reference will come in handy.

Given that, we can analyze the app.


There are eight modules in the app.

T_0; PRNG

The first module is basically a PRNG, which uses a fixed seed value 516562327.

T_0 ; PRNG Module
    %pushi/vec4 516562327, 0, 33;
    %store/vec4 v0x5614c9a85fc0_0, 0, 33; Set seed value
    %vpi_func 4 20 "$urandom" 32, v0x5614c9a85fc0_0 {0 0 0};
    %pushi/vec4 16, 0, 32;
    %mod;
    %pad/u 4; Generate random, using seed, truncate.
    %store/vec4 v0x5614c9a608a0_0, 0, 4; Store random value

With a fixed seed value, this module generates a total of six numbers. As the seed value is fixed, the generated random numbers are fixed.

T_1; Permutation

The second module is a permutation module and it's the longest, taking up more than the half of lines.

T_1 ;
    %wait E_0x5614c9a65a10; always @cnt
    %load/vec4 v0x5614c9a4d060_0; 
    %pad/u 32;
    %pushi/vec4 4, 0, 32;
    ;Routine that permutes numbers

Upon every change of cnt, which is incremented on every number read, this module permutes the password.

T_2; opens input file

This module just opens up and returns the file handle for key.txt

T_2 ;
    %pushi/vec4 2, 0, 3;
    %store/vec4 v0x5614c9a865f0_0, 0, 3;
    %pushi/vec4 0, 0, 5;
    %store/vec4 v0x5614c9a861c0_0, 0, 5;
    %pushi/vec4 0, 0, 9;
    %store/vec4 v0x5614c9a868c0_0, 0, 9; zero variables
    %vpi_func 3 30 "$fopen" 32, "key.txt", "r" {0 0 0}; open key.txt
    %store/vec4 v0x5614c9a86350_0, 0, 32; The handle for `key.txt`
    %load/vec4 v0x5614c9a86350_0;
    %cmpi/e 0, 0, 32;
    %jmp/0xz  T_2.0, 4;
    %vpi_call 3 32 "$display", "keyHandler was NULL" {0 0 0};
    %vpi_call 3 33 "$finish" {0 0 0}; error handling

T_3; read/evaluate each input and count successes

This module reads a single number on each line of key.txt.

T_3 ;
    %wait E_0x5614c9a66a20; always @(posedge clk)
    %vpi_func 3 38 "$fscanf" 32, v0x5614c9a86350_0, "%d\012", v0x5614c9a86430_0 {0 0 0};
    %store/vec4 v0x5614c9a86280_0, 0, 32; read and store a number from file, ending with newline. 
    %vpi_func 3 39 "$feof" 32, v0x5614c9a86350_0 {0 0 0};
    %nor/r;
    %flag_set/vec4 8;
    %jmp/0xz  T_3.0, 8;

After that, it increments cnt to trigger T_1

    %load/vec4 v0x5614c9a861c0_0;
    %pushi/vec4 1, 0, 5;
    %add;
    %store/vec4 v0x5614c9a861c0_0, 0, 5; v0x5614c9a861c0_0(cnt) += 1, this triggers permutation of the password

After module T_1 permutates the answer, it compares it with the input.

    %load/vec4 v0x5614c9a866d0_0; permutated answer
    %load/vec4 v0x5614c9a86430_0; your input number
    %parti/s 5, 0, 2;
    %cmp/e; is input == answer?
    %jmp/0xz  T_3.2, 4; 
    %load/vec4 v0x5614c9a868c0_0; input == answer
    %pushi/vec4 1, 0, 9;
    %add;
    %store/vec4 v0x5614c9a868c0_0, 0, 9; success += 1
T_3.2 ;
    %jmp T_3.1; input != answer

Upon spending all the numbers given as input, it checks how many correct answers were there.

T_3.0 ;
    %load/vec4 v0x5614c9a868c0_0; Success Count
    %pad/u 32;
    %cmpi/e 24, 0, 32; Check Success Count == 24
    %jmp/0xz  T_3.4, 4;
    %pushi/vec4 1, 0, 3;
    %assign/vec4 v0x5614c9a865f0_0, 0; assign passReg = b'1 
    %jmp T_3.5;
T_3.4 ;
    %vpi_call 3 49 "$display", "Success Count: %d\012", v0x5614c9a868c0_0 {0 0 0};
    %pushi/vec4 0, 0, 3;
    %assign/vec4 v0x5614c9a865f0_0, 0; assign passReg = b'0 

Given there are exactally 24 correct answers, it assigns 1 into passReg, for v0x5614c9a865f0_0. More and less would end up with 0.


we skip T_4, T_5. It's just a timer alternating between 0 and 1 every 50ns. T_4 initializes the timer, T_5 alternates it.

we also skip T_6. It just sets reset_n on all modules at the start.

T_7; check

This module just prints out if you got it right or not.

T_7 ;
    %wait E_0x5614c9a65700; Upon event E_0x5614c9a65700, which is reflects v0x5614c9a865f0_0(passReg)
    %load/vec4 v0x5614c9a86af0_0;
    %pad/u 32;
    %cmpi/e 1, 0, 32; Success == 1
    %jmp/0xz  T_7.0, 4;
    %vpi_call 2 26 "$display", "Success Auth!" {0 0 0}; You get flag
    %vpi_call 2 27 "$finish" {0 0 0};
    %jmp T_7.1;
T_7.0 ;
    %load/vec4 v0x5614c9a86af0_0;
    %pad/u 32;
    %cmpi/e 0, 0, 32;
    %jmp/0xz  T_7.2, 4;
    %vpi_call 2 30 "$display", "Failed To Auth" {0 0 0}; No flag 4 U
    %vpi_call 2 31 "$finish" {0 0 0};

Solution

The permutation routine is tricky to reverse, but we don't have to.

Here, the permutation routines' output range is limited with 5 bits.

v0x5614c9a866d0_0 .net "password", 4 0, L_0x5614c9a86d30;  5-bit answer
(...code omitted...)
    %load/vec4 v0x5614c9a86430_0;
    %parti/s 5, 0, 2; bit select and signed extension

However, we can use up to 1023 inputs as answer, which is about a square of possible answer count.

After some extensive testing, we found out that the permutation routine returns 1231 as the seventh number. Therefore, just spamming 1231 enough will allow us to solve the challenge. Yet, 1023 1231s returned about 2^5 successes, as expected. Tuning it down to 790 1231s did the trick.

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