A look back at a cybersecurity event that was small in scale but rich in technical depth: Auvergn’Hack. While everyone’s eyes were on the FIC, another event quietly made its mark—far from the hustle and bustle of Lille: Auvergn’Hack. A down-to-earth, tech-focused conference with hands-on workshops and talks in the morning, followed by a CTF in the afternoon. During the CTF, I focused on the reverse engineering challenge. This article is the write-up of the “driverlicence2” challenge. It’s follow but without technical link with the first one.

This challenge felt like a quick win — I have a feeling my method completely bypassed the intended way to solve it. Still, it was a great learning experience, and I’m looking forward to digging into driverlicence2 in the next write-up.

Challenge file (sha256sum : cd320bff9a01dfb1e805e393060060f6380d81964e75aa2310cdd9395b0cb038) : challenge

Solution 1

The challenge binary is a standard 64-bit Linux executable :

$ file driverlicence2
driverlicence2: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 4.4.0, BuildID[sha1]=0920879ef3ac8aa03d74acc5fcc4bc8c63e15cbf, not stripped

A simple strace reveals the flag. I think it’s just due to a character limitation :

$ strace ./driverlicence2
[...]
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=3, tv_nsec=0}, 0x7ffdad6e1560) = 0
write(3, "ZiTF{943fdce7b58602a1cf5b4d9e167"..., 39) = -1 EBADF (Mauvais descripteur de fichier)
close(3)                                = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f9fcb2c5000, 12288)           = 0
exit_group(0)                           = ?
+++ exited with 0 +++

I simply increased the character limit in strace—and boom, it’s a win :

clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=3, tv_nsec=0}, 0x7fff1be795e0) = 0
write(3, "ZiTF{943fdce7b58602a1cf5b4d9e1673028a}\n", 39) = -1 EBADF (Mauvais descripteur de fichier)
close(3)                                = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f15dbdf6000, 12288)           = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Solution 2

This last challenge was pretty straightforward, and I got a bit lucky. The idea to use strace came from my reverse engineering habits while doing Root-Me—sometimes, it really saves time.

Later, I went back and tried to solve it using the official method, just to learn more. For me, CTFs aren’t just about capturing the flag at all costs—they’re about having fun and learning as much as possible along the way.

Just like in driver licence 1, to navigate properly in Ghidra, I start by identifying the program’s base address:

$ gdb driverlicence2
(gdb) break main
Breakpoint 1 at 0xab80
(gdb) run 
Starting program: /home/gerboise/Téléchargements/driverlicence2 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, 0x000055555555eb80 in main ()
(gdb) info proc map
process 9556
Mapped address spaces:

Start Addr         End Addr           Size               Offset             Perms File 
0x0000555555554000 0x000055555555b000 0x7000             0x0                r--p  /home/gerboise/Téléchargements/driverlicence2 
0x000055555555b000 0x00005555555a4000 0x49000            0x7000             r-xp  /home/gerboise/Téléchargements/driverlicence2 
0x00005555555a4000 0x00005555555b4000 0x10000            0x50000            r--p  /home/gerboise/Téléchargements/driverlicence2 
0x00005555555b4000 0x00005555555b8000 0x4000             0x5f000            r--p  /home/gerboise/Téléchargements/driverlicence2 
0x00005555555b8000 0x00005555555b9000 0x1000             0x63000            rw-p  /home/gerboise/Téléchargements/driverlicence2 
0x00005555555b9000 0x00005555555da000 0x21000            0x0                rw-p  [heap] 

Now, once the base address is set, the next step is to locate the main function—since for some reason, Ghidra isn’t detecting any strings. We then observe that the challenge logic is located in the driverlicence2::main function : alt text

I’m looking near the end of the program for an address that’s likely to be reached during execution and that would allow me to dump the program’s memory after most of the instructions have run. 0x000055555555e7a9 looks like a solid candidate.

alt text

(gdb) b *0x55555555e7a9
Breakpoint 2 at 0x55555555e7a9
(gdb) c
Continuing.
[.] Loading circuit...
[.] Checking Track Surface...
[.] Preparing Flags...
[.] Heating Tire...
[.] Checking Pit Lane...
[.] Verifying pilot abilities...
[.] Loading Driver Licence...
[+] Pilot is ready to race
[+] Driver Licence Control In Progess
[+] Searching for Driver Licence...
Breakpoint 2, 0x000055555555e7a9 in driverlicence2::main ()

In programming, a string is a complex object. It’s usually instantiated and manipulated on the heap. In this case, the heap is located between 0x00005555555b9000 and 0x00005555555b9000 (as seen in the output of the info proc map command). So I’m going to dump the contents of the entire relevant memory section into a file.

(gdb) dump memory heap_dump.raw 0x00005555555b9000 0x00005555555da000

Bingo! The flag was indeed in the heap, and it was easy to spot using the strings command :

$ strings heap_dump.raw
4/ld-linux-x86-64.so.2
rlicence2
ZiTF{943fdce7b58602a1cf5b4d9e1673028a}
r Driver Licence...ssS
u *E
QS8|
=,o?
 Lane...!
602a1cf5b4d9e1673028a}
ZiTF{943fdce7b58602a1cf5b4d9e1673028a}

Conclusion

This last challenge was pretty straightforward, and I got a bit lucky. The idea to use strace came from my reverse engineering habits while doing Root-Me—sometimes, it really saves time.

Later, I went back and tried to solve it using the official method, just to learn more. For me, CTFs aren’t just about capturing the flag at all costs—they’re about having fun and learning as much as possible along the way.