# rev

## Challenges

<figure><img src="https://2268275695-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUrHD5lu5pQjrB9B8IR6W%2Fuploads%2FeZuC9DhcI01o7IQYUb0I%2Fimage.png?alt=media&#x26;token=de6d5f62-7ee2-4c45-90ce-61ed924a7a5d" alt=""><figcaption></figcaption></figure>

### hidden string

<figure><img src="https://2268275695-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUrHD5lu5pQjrB9B8IR6W%2Fuploads%2F90ijmhCJUm7u67vmXnLV%2Fimage.png?alt=media&#x26;token=01db71c9-b2d7-4c27-8f31-0d876c7ba8e3" alt=""><figcaption></figcaption></figure>

> This binary seems to be giving me some prompts but where are these strings coming from??
>
> **note: for the flag use the part of it that looks like it might sense. There is a slight bug in the challenge :)**
>
> [challenge](https://ctf.nullcon.net/files/b6e2842e32ade7a2f616f958dff1457f/challenge?token=eyJ1c2VyX2lkIjozNzY2LCJ0ZWFtX2lkIjoxNjQyLCJmaWxlX2lkIjoxMDl9.aLuxCA.1JZbsOKHreBq0qy5b2Akdx6hh2o)

{% hint style="success" %}
tl;dr: The binary stores its prompts and checks inside a **XOR-obfuscated string table** in <mark style="color:$danger;">`.data`</mark>. A verifier walks a byte program in <mark style="color:$danger;">`.data`</mark> as <mark style="color:$danger;">`(index, offset)`</mark> pairs, pulls characters from that table, XORs them with <mark style="color:$danger;">`0x61`</mark>, and compares against your input (also XORed with <mark style="color:$danger;">`0x61`</mark>). Dump the table, follow the pairs to reconstruct the flag, and wrap it as <mark style="color:$danger;">`ENO{…}`</mark>.&#x20;

⚠️There’s a tiny challenge bug that leaves padding pairs `(0,0)` at the end (which would add `www`); stop at <mark style="color:$danger;">`}`</mark> or the first `(`<mark style="color:$danger;">`0,0)`</mark> after you’ve started constructing, this is later announced in the announcement but it did tank my accuracy a lil bit.🐧
{% endhint %}

{% embed url="<https://binary.ninja/>" %}

* My initial reconnaissance didn't yield anything meaningful, strings, file carve or obj dump didn't really provide much information due to the output is obfuscated.. So ...its binary ninja huh, I just like the theme and the layout of this app so much. Open it and we jumped to <mark style="color:$danger;">`main`</mark>, and noticed the helper <mark style="color:$danger;">`sub_4015e0`</mark> and the leaf <mark style="color:$danger;">`sub_401550`</mark>. All three touched <mark style="color:$danger;">`data_404160`</mark>, <mark style="color:$danger;">`data_404168`</mark>, and <mark style="color:$danger;">`data_404170`</mark>,
* They all interact with the data section which...is the flag being scatter everywhere?
* A three-column table in <mark style="color:$danger;">`.data`</mark><mark style="color:$danger;">.</mark> Later figured but the way I called these are per-entry <mark style="color:$danger;">`(pointer, length, xorKey)`</mark> layout.
* See it in <mark style="color:$danger;">`Binary Ninja`</mark> the function and the data arrays:

<figure><img src="https://2268275695-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUrHD5lu5pQjrB9B8IR6W%2Fuploads%2FDoV8qJQlYmREJab18fW2%2Fimage.png?alt=media&#x26;token=d0ed3480-3d8c-4c33-b2cc-6ebc118e4c36" alt=""><figcaption></figcaption></figure>

{% tabs %}
{% tab title="sub\_401550 (printer)" %}
**LOGICALLY SPEAKING**:

* <mark style="color:$danger;">`sub_401550(id)`</mark> XOR-decodes the id-th string with its key, prints `length-1` bytes, then XORs it back. BN shows the table layout clearly:

<pre class="language-c"><code class="lang-c">int64_t sub_401550(int64_t arg1) {}
    uint64_t count
    
    if (*(arg1 * 0x18 + 0x404168) == 0)
        count = -1
    else
        int64_t rax_1 = 0
        int64_t rdx_3
        
        do
<strong>            char* rdx_2 = (&#x26;data_404160)[arg1 * 3] + rax_1
</strong>            rax_1 += 1
<strong>            *rdx_2 ^= *(arg1 * 0x18 + 0x404170)
</strong><strong>            rdx_3 = *(arg1 * 0x18 + 0x404168)
</strong>        while (rax_1 u&#x3C; rdx_3)
        
        count = rdx_3 - 1
    
    fwrite(buf: (&#x26;data_404160)[arg1 * 3], size: 1, count, fp: stdout)
    int64_t i = 0
    
    if (*(arg1 * 0x18 + 0x404168) != 0)
        do
            char* rdx_5 = (&#x26;data_404160)[arg1 * 3] + i
            i += 1
            *rdx_5 ^= *(arg1 * 0x18 + 0x404170)
        while (i u&#x3C; *(arg1 * 0x18 + 0x404168))
    
    return i
}
</code></pre>

* The stride is therefore **0x18** per entry; valid IDs are **0..0x2f** (guarded in both the printer and checker).

<table><thead><tr><th width="243">Field (per id)</th><th>Address pattern</th><th>Meaning</th></tr></thead><tbody><tr><td><code>(&#x26;data_404160)[id * 3]</code></td><td>base <code>0x404160</code> + <code>id * 0x18</code> + <code>0x00</code></td><td>pointer to bytes</td></tr><tr><td><code>*(id * 0x18 + 0x404168)</code></td><td>base <code>0x404168</code> + <code>id * 0x18</code></td><td>length (incl. <code>\0</code>)</td></tr><tr><td><code>*(id * 0x18 + 0x404170)</code></td><td>base <code>0x404170</code> + <code>id * 0x18</code></td><td>XOR key (1 byte)</td></tr></tbody></table>
{% endtab %}

{% tab title="main" %}

<pre class="language-c"><code class="lang-c">int32_t main(int32_t argc, char** argv, char** envp)
    void* fsbase
    int64_t rax = *(fsbase + 0x28)
    int64_t var_178 = 0
    int64_t var_170
    __builtin_memcpy(dest: &#x26;var_170,
<strong>        src: "\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x"
</strong>    "00\x00\x00\x00\x00",
        count: 0x18)
<strong>    sub_4015e0(&#x26;var_178, 4, 1)
</strong>    int64_t var_188 = 4
    int64_t var_180 = 5
<strong>    sub_4015e0(&#x26;var_188, 2, 0)
</strong>    fputc(c: 0x20, fp: stdout)
    int32_t buf
    int32_t result

    if (fgets(&#x26;buf, n: 0x100, fp: stdin) == 0)
        result = 1
    else
        result = 0
        *(&#x26;buf + strcspn(&#x26;buf, "\r\n")) = 0

<strong>        if (buf == 0x7b4f4e45)
</strong>            uint64_t rax_7 = strlen(&#x26;buf)

<strong>            if (*(&#x26;buf + sx.q((rax_7 - 1).d)) == 0x7d)
</strong>                if (rax_7 != 0)
                    int32_t* i = &#x26;buf

                    do
                        uint32_t rcx_1 = zx.d(*i)
                        uint64_t rdx_2 = zx.q(0x61 - &#x26;buf + i.d) &#x26; 3
                        char rsi_1 = rcx_1.b ^ 0x61
<strong>                        # 0x61 🥀🥀🥀🥀🥀
</strong><strong>                        if (rdx_2 != 1 &#x26;&#x26; rdx_2 == 3)
</strong><strong>                            rsi_1 = (rcx_1.b | 0x61) &#x26; (not.d(rcx_1 &#x26; 0x61)).b
</strong>
                        *i = rsi_1
                        i += 1
                    while (i != &#x26;buf + rax_7)

                int64_t var_158

                if (rax_7 == 0 || rax_7 u&#x3C;= 5)
                label_401397:
<strong>                    __builtin_memcpy(dest: &#x26;var_158,
</strong><strong>                        src: "\x06\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x"
</strong><strong>                    "00\x26\x00\x00\x00\x00\x00\x00\x00\x27\x00\x00\x00\x00\x00\x00\x00"
</strong><strong>                    "28\x00\x00\x00\x00\x00\x00\x00",
</strong>                        count: 0x28)
                    result = 2
<strong>                    sub_4015e0(&#x26;var_158, 5, 1)
</strong>                else
                    void var_124
                    void* i_1 = &#x26;var_124
                    void* const r10_1 = &#x26;data_402021
                    void var_117

                    do
                        uint64_t r9_2 = zx.q(*(r10_1 - 1))
                        char rbx_1 = 0

                        if (r9_2 u&#x3C;= 0x2f)
                            uint64_t rbp_1 = zx.q(*r10_1)
<strong>                            int64_t rax_11 = *(r9_2 * 0x18 + 0x404168)
</strong>
                            if (rbp_1 u&#x3C; rax_11 - 1)
                                if (rax_11 == 0)
<strong>                                    rbx_1 = *((&#x26;data_404160)[r9_2 * 3] + rbp_1) ^ 0x61
</strong>                                else
                                    int64_t rax_12 = 0
                                    int64_t rcx_8

                                    do
<strong>                                        char* rcx_7 = (&#x26;data_404160)[r9_2 * 3] + rax_12
</strong>                                        rax_12 += 1
                                        *rcx_7 ^= *(r9_2 * 0x18 + 0x404170)
                                        rcx_8 = *(r9_2 * 0x18 + 0x404168)
                                    while (rax_12 u&#x3C; rcx_8)

<strong>                                    rbx_1 = *((&#x26;data_404160)[r9_2 * 3] + rbp_1) ^ 0x61
</strong>
                                    if (rcx_8 != 0)
                                        int64_t j = 0

                                        do
<strong>                                            char* rcx_10 = (&#x26;data_404160)[r9_2 * 3] + j
</strong>                                            j += 1
                                            *rcx_10 ^= *(r9_2 * 0x18 + 0x404170)
                                        while (j u&#x3C; *(r9_2 * 0x18 + 0x404168))

                        if (*i_1 != rbx_1)
                            goto label_401397

                        i_1 += 1
                        r10_1 += 2
                    while (&#x26;var_117 != i_1)

                    var_158 = 7
                    int64_t var_150_1 = 5
                    sub_4015e0(&#x26;var_158, 2, 1)

    *(fsbase + 0x28)

    if (rax == *(fsbase + 0x28))
        return result

    __stack_chk_fail()
    noreturn

</code></pre>

{% endtab %}

{% tab title="sub\_4015e0" %}

```c
void sub_4015e0(int64_t* arg1, int64_t arg2, int32_t arg3)
    int64_t* rbx = arg1

    while (true)
        int64_t rdi = *rbx

        if (rdi u<= 0x2f)
            sub_401550(rdi)

        rbx = &rbx[1]

        if (rbx == &arg1[arg2])
            break

        fputc(c: 0x20, fp: stdout)

    if (arg3 != 0)
        return fputc(c: 0xa, fp: stdout) __tailcall
```

{% endtab %}

{% tab title="binary ninja" %}

<figure><img src="https://2268275695-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUrHD5lu5pQjrB9B8IR6W%2Fuploads%2FgiQiboLoDhWKfRCmcpkg%2Fimage.png?alt=media&#x26;token=2a59de2e-4493-49ff-8471-fe3187e840a3" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

Focus on the highlighted lines (important piece), phewwwww, that's a lot of low level code (\tuf), now lets run the binary, we will see prompts like:

```bash
❯ ./challenge
welcome to ENO challenge
enter flag
```

* We already know <mark style="color:$danger;">`main`</mark> calls <mark style="color:$danger;">`sub_4015e0`</mark> with small integer arrays. Each element is an **ID** into a string table. <mark style="color:$danger;">`sub_4015e0`</mark> prints the corresponding strings with spaces, and optionally a newline:

```c
// prints: welcome to ENO challenge (where it all starts)
__builtin_memcpy(&var_170, "\x00\x00...\x03", 0x18);
sub_4015e0(&var_178, 4, 1);
```

* Inside that, <mark style="color:$danger;">`sub_401550(id)`</mark> does it things (explained above)
* We also know that the prefix check ENO{???} is also included:

```c
if (buf == 0x7b4f4e45) {           // 0x7b 4f 4e 45
    ...
    if (*(&buf + sx.q((rax_7 - 1).d)) == 0x7d) { ... }  // 0x7d = '}'
}
```

* The machine is **little-endian**, so <mark style="color:$danger;">`0x7b4f4e45`</mark> in a 32-bit register corresponds in memory to bytes (you get the idea):
* ```c
  45 4E 4F 7B  == 'E' 'N' 'O' '{'
  ```
* Now we know where it all starts...to not bore you, but after this, you pretty much just traced the line after the ENO{ to see what encryption logic is being applied, and reverse it; The moment you have a string that pass the check "enter flag", done.

{% hint style="info" %}
And with all that information, the solve script follow these steps:
{% endhint %}

* First -- open the string table in <mark style="color:$danger;">`.data`</mark> (starting at <mark style="color:$danger;">`0x4160`</mark>), treating each 24-byte row as <mark style="color:$danger;">`(ptr, len, key)`</mark>&#x20;
* XOR-decoding (0x61) (they cancels out) the bytes so we can read the hidden words.
* Then it walks the byte stream at <mark style="color:$danger;">`0x2020`</mark> as <mark style="color:$danger;">`(index, offset)`</mark> pairs and plucks the corresponding characters out of that table to rebuild the message.&#x20;
  * It stops on the first `}` or the first `(0,0)` pair (to skip the padding that would add <mark style="color:$danger;">`www`</mark>) <-- that <mark style="color:$danger;">`www`</mark> is what the announcement meant.
* Finally, wraps the result with `ENO{…}` for full flagasano.

```python
from pwn import ELF, u64

# da table warudooooooooo
elf = ELF("./challenge", checksec=False)
TABLE = 0x4160
PAIRS = 0x2020
ENTRY_SZ = 0x18
MAX_IDX = 0x30

# upper bound for safe reads
max_addr = max(sec.header.sh_addr + sec.header.sh_size for sec in elf.sections)


def ok(a, n=1):
    return 0 <= a <= max_addr - n


# decode table rows: (ptr,len,key) with stride 0x18
decoded, i = {}, 0
while i < MAX_IDX:
    row = TABLE + i * ENTRY_SZ
    if not ok(row, ENTRY_SZ):
        break
    ptr = u64(elf.read(row + 0x00, 8))
    length = u64(elf.read(row + 0x08, 8))
    key = elf.read(row + 0x10, 1)[0]
    if length and ok(ptr, length):
        buf = bytearray(elf.read(ptr, max(length, 0x40)))
        for j in range(len(buf)):
            buf[j] ^= key
        decoded[i] = bytes(buf)
    i += 1

# walk (idx,off) pairs
pairs = elf.read(PAIRS, 0x100)
chars = []
for k in range(0, 64):
    idx, off = pairs[2 * k], pairs[2 * k + 1]
    if (idx, off) == (0, 0) and chars:
        break  # sentinel to avoid trailing 'www'
    s = decoded.get(idx)
    if not s:
        break
    if off >= max(0, len(s) - 1):
        break
    chars.append(chr(s[off]))

body = "".join(chars)
flag = (
    f"ENO{{{body}}}"
    if not body.startswith("ENO{")
    else (body if body.endswith("}") else body + "}")
)
print(flag)
```

* <mark style="color:$success;">**`ENO{stackxorrocks}`**</mark>
