IRCWare#

IRCWare is a medium difficulty reverse engineering challenge on Hack The Box available at LINK. It is a Linux x86-64 ELF file.

ircware: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, stripped

When we run the binary we get a stdout message:

EXCEPTION! ABORT

Let us start then by examining main function, which is conveniently already under ‘start’ symbol. Screenshot of main function as opened in Ghidra Looking at RAX values, syscall at #1 is 0x3e which corresponds to sys_getrandom and at #3 we first have 0x1 (sys_write), which prints error message we had seen, followed by 0x0 sys_exit. Let’s closer examine #2 then to see why exactly we are failing. Our conditional if depends on return value from function at 0x40028f: Screenshot of FUN_0040028f function as decompiled in Ghidra It calls 2 syscalls then returns. Syscall #1 is 0x29 (sys_socket), then #2 is 0x2a (sys_connect). From that we can deduce there is some networking at play here. To speed up reversing, let’s use strace on the binary. Output of terminal strace ircware command We can see that it is trying to read IP 127.0.0.1 on port 8000. Let’s set up local server to handle the connection with command:

nc -l -p 8000

Once we rerun the binary, success! We receive some data:

NICK ircware_4207
USER ircware 0 * :ircware
JOIN #secret

If we look closer at ’entry’ function, then by examining RAX value passed to FUN_004002fb and 0x1 syscall (write) used inside the functions we can deduce that this is where out received data is coming from. Next we have an infinite do while loop. We have two functions there FUN_004002d3 and FUN_00400349. We can quite quickly deduce that the first one is responsible for reading data from our listener and passing it to the latter where main logic happens, let’s call it then accordingly ‘main_logic. This is also the most interesting one for us in this binary. Screenshot of main_logic function as decompiled in Ghidra Unfortunately for us, this function is kind of a mess in Ghidra. Nevertheless we can guess some information already. There are few placed in main_logic that look suspiciously like commands that are accepted by it, namely #1, #2, #3. Let’s test it out empirically.

NICK ircware_6762
USER ircware 0 * :ircware
JOIN #secret
PING :
PONG :

PRIVMSG #secret :@pass
PRIVMSG #secret :@pass password
PRIVMSG #secret :Rejected
PRIVMSG #secret :@exec cmd
PRIVMSG #secret :Requires password
PRIVMSG #secret :@flag
PRIVMSG #secret :Requires password

We have in total 4 commands:

  • PING : - which responds with PONG :, but is rather useless for us
  • PRIVMSG #secret :@pass <password>
  • PRIVMSG #secret :@exec <command>
  • PRIVMSG #secret :@flag

PRIVMSG #secret :@flag is most likely the one we need to run to get our flag, but it is password protected. Let’s understand PRIVMSG #secret :@pass then. There is interesting logic at the bottom of decompiler output, but it is again a mess. Screenshot of main_logic password checking logic decompilation in Ghidra We can see some Accepted/Rejected prints (I have already took the liberty to rename function at 0x400485 to send_response) #1 and there are some checks at #2 also. To untangle this mess we need to look at assembly instead. Screenshot of main_logic password checking logic decompilation in Ghidra This is an interesting comparison against string “RJJ3DSCP”. We see that it is being compared to bytes in AL, after some operations done on the register - AL+0x11-0x5a+40 which equals to -0x9 and matches the value Ghidra was displaying for us at #2 in previous screenshot. Let’s see what happens if we shift “RJJ3DSCP” by 9 - I used this Caesar cipher tool since this is the same operation. What we get from it is “ASS3MBLY”, this is too neat to be a coincidence… Let’s try our password then: Success Great success!