0xLaugh Rev Challenges

nano

nano is an elf 64bit file so we will use linux with ida in analyzing it

It comes with simple antidisassembly as discussed in PMA lab15 we can replace the bytes from 134E to 1352 with NOPs replace 74 03 75 01 E8 with 90’s

../_images/Screenshot_114.png
fixed= open("nano", "rb").read().replace(b"\x74\x03\x75\x01\xe8", b"\x90\x90\x90\x90\x90")
open("cleaned_nano","wb").write(fixed)

first is a cheesy check function

../_images/Screenshot_212.png

it is XOR and we are given key and flag and if we xor we get the flag?

../_images/Screenshot_412.png

No we get watch a rickroll video

../_images/Screenshot_512.png

Looking again at ida we see it attempts to read from address zero with gives SIGSEGV (segmentation fault)

../_images/Screenshot_612.png

Which we get when we run gdb-peda with breaking at check function break check

../_images/Screenshot_712.png

Looking at ida, note that the check happens in a child process

../_images/Screenshot_83.png

And the parent process catches that exception using waitpid status loc, if there is error break and if child exited with specific status do the following The status is WTERMSIG(status) which checks SEGSEGV from check2

../_images/Screenshot_92.png

it calculates some values (note pid[1] is just zero) the calls a ptrace syscall which has ability to trace a pid and get registers and set registers

../_images/Screenshot_103.png ../_images/Screenshot_115.png

following same or’ed value in ptrace calls we see the value is set r12

../_images/Screenshot_122.png ../_images/Screenshot_132.png

Which is the register that contains our KEY, so we write a python script to replicate the replacing of KEY bytes then xor it with the FLAG

import pwn

key=""
flag = "0C 5C 60 20 69 63 64 0F 4F 1E 33 3A 68 2A 7C D9 D5 D0 C9 E7 C3 F0 BC AB 9B D7 98 8B AF B0 F8 47 49 16 49 68"

for i in range(1,37):

    v9 = (i>>5)|(8*i)^0xCA
    v9 ^=0xFE
    v9 &=0xFF #to constrain for 8 bit
    key =key +format(int(str(hex(v9))[2:],16),"02X") + " "
print(pwn.xor(bytes.fromhex(key),bytes.fromhex(flag)))
../_images/Screenshot_151.png

easy login

We are given two files, both are elf 64bit

../_images/Screenshot_161.png

in the easy login binary we get user pass token

../_images/Screenshot_171.png

so we check the other binary

../_images/Screenshot_181.png
In ida we see set our goal to get final Correct tokan!
So first we see the first token part (v4) and fixed byte array is passed to sub 1159 which changes it
Then check token parts to if ( v4 == 4068142527 && v5 == 3976246892 )
../_images/Screenshot_191.png

inside the function is bunch of calcs then set in our first token part, so we reverse the process with final value being if ( v4 == 4068142527 && v5 == 3976246892 )

../_images/Screenshot_201.png
#include <stdio.h>

unsigned int reverse_sub_6519F8FD0159(unsigned int *a1, unsigned int *a2) {
    unsigned int v4 = 3337565984;
    int i;

    printf("%u",a1[1]);
    for (i = 31; i >= 0; --i) {
        a1[1] -= ((*a1 >> 5) + a2[3]) ^ (*a1 + v4) ^ (16 * *a1 + a2[2]);
        v4 += 1640531527;
        *a1 -= (a1[1] + v4) ^ (16 * a1[1] + *a2) ^ ((a1[1] >> 5) + a2[1]);
    }

    return *a1;
}

int main() {
    unsigned int v4 = 3337565984;
    unsigned int v5 = 0xED00B66C;

    unsigned int a1 = 0xF27AEDBF;
    unsigned int a2[] = {19088743, 2309737967, 4275878552, 1985229328};

    v4 = reverse_sub_6519F8FD0159(&a1, a2);

    puts("Token generation result:");
    printf("%u_%u\n", v4 , v5 );

    return 0;
}
Now we got the token we still need the user and pass, back to easy login binary
we get this code, where the user isn’t used, and the pass and token are passed to function then onto second function which base64
../_images/Screenshot_213.png

Which is then compared to pDG/SbSehGM2l16sRzFmxRDZNCti2PNXzY9Z

../_images/Screenshot_231.png ../_images/Screenshot_221.png

opening the first function used we see it is a typical RC4 To recognize RC4 use tools like capa ,sigsrch or its pattern as following:-

There’s usually 3 loops
2x 0x100 iterations: one filling the buffer with values from 0 to ff
One going over the array swapping values
And a 3rd producing the keystream
It is a very recognisable pattern
../_images/Screenshot_241.png ../_images/Screenshot_251.png

then we use the token as rc4 key and we get the flag

../_images/Screenshot_261.png ../_images/Screenshot_271.png

dance

It also has antidisassembly so we will use same code

fixed= open("dance", "rb").read().replace(b"\x74\x03\x75\x01\xe8", b"\x90\x90\x90\x90\x90")
open("cleaned_dance","wb").write(fixed)

it will pass our argument (flag) to function sub_13C7 to a child process with the parent ready to handle exception with ptrace, another nanomite

../_images/Screenshot_291.png

there is a lot of functions, so running capa we find crc32 is used at 2511

../_images/Screenshot_301.png ../_images/Screenshot_312.png

going into 13C7 we first need to fix ptrace argument to know where it set regs using ida enum

../_images/Screenshot_321.png

and where it is setting registers

../_images/Screenshot_331.png
another fork, in the child starts with ptrace traceme we can expect exception handling here
So what happens here is , it creates a file descriptor then passes “Hello, that is one key for you..” and nice_move_:) to sub_23FA which then passed to sub_246D through v3
../_images/Screenshot_341.png

Going into sub_246D, its chacha! (i used chatgpt here to recoginze the algorithm xD)

../_images/Screenshot_351.png

googling its implementation check

we see it is first initialized with key 2nd arg , nonce 3rd arg
ChaChaInitialize(&chaInfo, key, nonce, &counter, NUMROUNDS);

Then call the encryption/decryption

ChaChaEncrypt(&chaInfo, plainBufferLen, buffer);

and we got the clear picture

../_images/Screenshot_361.png

Going to 50C0 address and getting the data

../_images/Screenshot_371.png

and we get an elf file

../_images/Screenshot_381.png

the elf file contains a lot of cc (int 3 ) which will cause sigtrap and that where the ptrace comes in from the parent

../_images/Screenshot_391.png

back to the parent, we get this beutiful code where it waits for the exception cc to happen

../_images/Screenshot_401.png

lets dissect it, first check v19 & v18 and function sub_1255 with passed third argument bunch of cc

../_images/Screenshot_421.png
checking sub_1255, we see it uses PTRACE_POKEDATA which from check_here Copy the word data to the address addr in the tracee’s memory.
So in the loop it starts from address a2 which is v18 that was passed till a4 which is v19 and writes bunch of CC’s
../_images/Screenshot_431.png
we can infer that this is a write to pid function that write data from given address to another address, it is overwriting the previous address with CC’s
it is antidebugging technique so we can’t dump the written data it is overwriting instructions as it goes
../_images/Screenshot_441.png

Then it calls ptrace with GETREGS then calls crc32

../_images/Screenshot_451.png
we need to calculate what is gotten with ptrace, so we know the ptrace GETREGS call’s last argument is a registery structure
ptrace(PTRACE_GETREGS, child, NULL, &regs);
we get its address relative to stack in assembly
../_images/Screenshot_461.png
The RVA to rbp of registry structure is 544, then after the call to ptrace it get rbp-416
the differene is 544-416=128, and each value is 8 byte (64 bit), 128/8 =16, so it got the registery at offset 16
to get what resides at that offset run
cat /usr/include/x86_64-linux-gnu/sys/reg.h

and we get the rip registery

../_images/Screenshot_471.png
Now we can get the full picture, it gets the rip where the exception happened masks it with 0xFFF, runs crc32 to it
Then the index starting from 0 , the loop will search for the correct index where ** v14 != (_DWORD *)&unk_88A0[24 * some_index]* Condition is not true where v14 = crc32(rip) , then write data using that index offset from 88A0 , with size (last argument) into where the rip points to
../_images/Screenshot_481.png

set 88A0 type to unsigned __int8[16512] to fix the indexing, also fixing my mistake of curr_address to size

../_images/Screenshot_491.png
To understand the 88A0 structure, if we set some_index to zero from v14 != *(_DWORD *)&byte_88A0[24 * some_index] ,
we get that v14 (crc32(rip)) is first element in 88A0 structure, and we get its size is 4 bytes from this writeToPID(pid, rip_reg - 1, &byte_88A0[24 * some_index + 5], byte_88A0[24 * some_index + 4]); As next element is after 4 bytes then after 5 bytes
so The size to be written is at after 4 bytes and to be written is after 5 bytes
../_images/Screenshot_501.png

so 41 57 is an instruction which is push r15

../_images/Screenshot_513.png

we got all data we need, we just need to write code to get this 88A0 fixes the order of instructions and replace those with the CC’s in the elf file we got

import struct
import io
from zlib import crc32
from pwn import u64, p32


file= open("cleaned_dance", "rb")
file.seek(0x78a0)
crc_table = {}
table_88a0 = {}

#Rainbow Table for crc32(rip)
for i in range(0xfff+1):
        crc_table[crc32(p32(i))] = i
        #>>> p32(5)
        # b'\x05\x00\x00\x00' to bytes



while 1:


    # "<" indicates little-endian byte order. This means the least significant byte is stored first in memory.
    # I represents an unsigned integer of 4 bytes.
    # B represents an unsigned char of 1 byte.
    # 19B represents 19 unsigned chars (each of 1 byte)
    # intotal 24 where each part is 24 bytes &byte_88A0[24 * some_index + 5]
    code =struct.unpack("<IB19B",file.read(24))
    # print(code)
    crc = code[0]
    size = code[1]
    insn = bytes(code[2:])

    if crc == 0 and size ==0:
        break

    table_88a0[crc_table[crc]] = insn[:size]

elffile=open("out.elf","rb").read() #from cyberchef
elf_fp = io.BytesIO(elffile)
for addr,insns in table_88a0.items():
    elf_fp.seek(0x1000+addr)
    elf_fp.write(insns)
open("out2.elf","wb").write(elf_fp.getvalue())

Then we see the flag is passed to the generated binary with calling the function dance_with_me

../_images/Screenshot_521.png

opening the new binary in ida to dance_with_me function we see another chacha used

../_images/Screenshot_531.png

Decrypting wih chacha in cyberchef

https://gchq.github.io/CyberChef/#recipe=From_Hex(‘Auto’/disabled)ChaCha(%7B’option’:’Hex’,’string’:’6C7BB1EE0A4C479DEDD75BBCD243DD401DB277B8356E594BF86326D7E250EDDB’%7D,%7B’option’:’Hex’,’string’:’96BFEBCA8E7CFBBCD972A853’%7D,0,’20’,’Hex’,’Hex’)From_Hex(‘Auto’)&input=QjdFMDVERTg2NkFFQUU2QUQ5MEYzQTU3NTdDQkI3N0JGRTQ2OUEwQUY4MkZGODYzNUM1RUJDOEJGNjc0RjU2NjYxOTdGMTExQzU2OTZDRDVEQkREMjAyNDM2OEE2QzJG

../_images/Screenshot_541.png