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
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
it is XOR and we are given key and flag and if we xor we get the flag?
No we get watch a rickroll video
Looking again at ida we see it attempts to read from address zero with gives SIGSEGV (segmentation fault)
Which we get when we run gdb-peda with breaking at check function break check
Looking at ida, note that the check happens in a child process
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
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
following same or’ed value in ptrace calls we see the value is set r12
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)))
easy login
We are given two files, both are elf 64bit
in the easy login binary we get user pass token
so we check the other binary
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 )
#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;
}
Which is then compared to pDG/SbSehGM2l16sRzFmxRDZNCti2PNXzY9Z
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:-
then we use the token as rc4 key and we get the flag
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
there is a lot of functions, so running capa we find crc32 is used at 2511
going into 13C7 we first need to fix ptrace argument to know where it set regs using ida enum
and where it is setting registers
Going into sub_246D, its chacha! (i used chatgpt here to recoginze the algorithm xD)
googling its implementation check
ChaChaInitialize(&chaInfo, key, nonce, &counter, NUMROUNDS);
Then call the encryption/decryption
ChaChaEncrypt(&chaInfo, plainBufferLen, buffer);
and we got the clear picture
Going to 50C0 address and getting the data
and we get an elf file
the elf file contains a lot of cc (int 3 ) which will cause sigtrap and that where the ptrace comes in from the parent
back to the parent, we get this beutiful code where it waits for the exception cc to happen
lets dissect it, first check v19 & v18 and function sub_1255 with passed third argument bunch of cc
Then it calls ptrace with GETREGS then calls crc32
ptrace(PTRACE_GETREGS, child, NULL, ®s);
cat /usr/include/x86_64-linux-gnu/sys/reg.h
and we get the rip registery
set 88A0 type to unsigned __int8[16512] to fix the indexing, also fixing my mistake of curr_address to size
so 41 57 is an instruction which is push r15
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
opening the new binary in ida to dance_with_me function we see another chacha used
Decrypting wih chacha in cyberchef