Breaking into my Oracle VM
As many people know, Oracle Cloud has a very generous free tier. If you’re lucky and manage to bag yourself a slot, you can have yourself a 4 core, 24GB of RAM ARM64 VPS for free with up to 200GB of storage and a boatload of bandwidth available.
However, there is a catch… If you’re an idiot and create a VM without the right private keys assigned… That VM is useless. Or if you decide to nixos-infect a server without setting up your root SSH keys first… you can also lock yourself out, ask me how I know…
“Just recreate it with the right key and you’re sorted, right?”
Yeah, good luck with that. Since it’s a free tier product, people absolutely bash the API over and over to grab hold of one, so if you lose yours, you have to join the API bashers to get one back.
Plus, this way is way more fun… Wow, I’m really flexing the autism here…
The Plan#
The plan is simple. Fix it.
Okay, but seriously, the plan involves some dodgy tricks. Most people recommend using the Oracle Cloud Agent and the serial console to inject commands, but if you chose a custom distro and the agent isn’t running, you’re SOL.
So the plan is to do the following:
- Break Grub (Yep, really)
- Manually boot the OS into single user mode, or initramfs
- Inject an SSH key into authorized keys
- …
- Profit
Breaching the firewall#
Go to your instance in Oracle Cloud and under the OS Management tab, scroll down to the console connection section. You can choose to do a cloud shell or a local connection. The cloud shell is much easier, but it does ignore some keyboard shortcuts and messes with some of the keypresses. I’ll be using the local connection from a Mac here.
Click on create console connection and download the provided key pair. You can use your own and paste it in as long as it is an RSA key; it won’t accept an ed25519 key here.
Once created, click the three dots to the right of the entry shown in the console connection list and click copy serial console connection for Linux/macOS.
ssh -o ProxyCommand='ssh -W %h:%p -p 443 ocid1.instanceconsoleconnection.oc1.uk-london-1.anwgiljr2u2dlhqcrtfk3xh22wdvrrh4jroafhquc4pyxiuljxaiiikpt2zq@instance-console.uk-london-1.oci.oraclecloud.com' ocid1.instance.oc1.uk-london-1.anwgiljr2u2dlhqco5dmdf6qlu3ofwus3s4n7lvqtdfiacjyhsubthjefqqq
This may look like pure Linux waffle, but it is simply an SSH session… within an SSH session. But the command given here won’t work like this, so it needs to be tweaked.
We need to add the following:
- -i ./ssh-key-2026-01-29.key
- -o HostKeyAlgorithms=+ssh-rsa
- -o PubkeyAcceptedKeyTypes=+ssh-rsa
You need to add the path to the downloaded key and specify that you will use an RSA key. Without this, the session will try to use your own SSH key (which is fine if you chose that option).
ssh -i ./ssh-key-2026-01-29.key -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa -o ProxyCommand='ssh -i ./ssh-key-2026-01-29.key -W %h:%p -p 443 ocid1.instanceconsoleconnection.oc1.uk-london-1.anwgiljr2u2dlhqcrtfk3xh22wdvrrh4jroafhquc4pyxiuljxaiiikpt2zq@instance-console.uk-london-1.oci.oraclecloud.com' ocid1.instance.oc1.uk-london-1.anwgiljr2u2dlhqco5dmdf6qlu3ofwus3s4n7lvqtdfiacjyhsubthjefqqq
Once connected, you won’t see much. Press Enter and you’ll be greeted by your login prompt.
=================================================
IMPORTANT: Use a console connection to troubleshoot a malfunctioning instance. For normal operations, you should connect to the instance using a Secure Shell (SSH) or Remote Desktop connection. For steps, see https://docs.cloud.oracle.com/iaas/Content/Compute/Tasks/accessinginstance.htm
For more information about troubleshooting your instance using a console connection, see the documentation: https://docs.cloud.oracle.com/en-us/iaas/Content/Compute/References/serialconsole.htm#four
=================================================
The authenticity of host 'ocid1.instance.oc1.uk-london-1.anwgiljr2u2dlhqco5dmdf6qlu3ofwus3s4n7lvqtdfiacjyhsubthjefqqq (<no hostip for proxy command>)' can't be established.
RSA key fingerprint is SHA256:sxXcXVgIwDWKc0MfVB4PIgPSeuzUaI8Zlj2nZuvA0Ao.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'ocid1.instance.oc1.uk-london-1.anwgiljr2u2dlhqco5dmdf6qlu3ofwus3s4n7lvqtdfiacjyhsubthjefqqq' (RSA) to the list of known hosts.
ls03 login:
Onto the dodgy bit…
Breaking GRUB#
Yeah, that’s not a typo… We need to break Grub for the next step. Let me explain:
A lot of cloud VM images use GRUB by default and have the GRUB timeout value set to zero. This means we can’t interrupt the boot sequence like you would on a desktop install to edit the kernel parameters for single user mode. Here, the best option is to use the EFI console to stop GRUB from reading its config file so we get dropped into a basic GRUB prompt. From here, we can boot the kernel directly, or at least reach initramfs (essentially a mini Linux install) and inject our SSH key.
Go into your Oracle console once again and under actions, select reboot and reboot your instance (I choose force reboot as it’s faster). Once you click this, immediately switch back to your serial console and hold ESC.
After a few seconds, your console should reset and you will end up in the UEFI menu.
KVM Virtual Machine
virt-4.2 2.00 GHz
1.5.1 24576 MB RAM
Select Language <Standard English> This is the option
one adjusts to change
> Device Manager the language for the
> Boot Manager current system
> Boot Maintenance Manager
Continue
Reset
^v=Move Highlight <Enter>=Select Entry
From here, go to Boot Manager and then EFI Internal Shell
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS0: Alias(s):HD0a1p:;BLK2:
PciRoot(0x0)/Pci(0x12,0x7)/Pci(0x0,0x0)/Scsi(0x0,0x1)/HD(15,GPT,E6CC91AA-45A9-4664-9DFC-768429C869FE,0x800,0x31801)
BLK3: Alias(s):
VenHw(93E34C7E-B50E-11DF-9223-2443DFD72085,00)
BLK0: Alias(s):
PciRoot(0x0)/Pci(0x12,0x7)/Pci(0x0,0x0)/Scsi(0x0,0x1)
BLK1: Alias(s):
PciRoot(0x0)/Pci(0x12,0x7)/Pci(0x0,0x0)/Scsi(0x0,0x1)/HD(1,GPT,EF7A69B2-4281-45E2-9847-9BC5FD0D04BB,0x32800,0x12BCD7DF)
Press ESC in 4 seconds to skip startup.nsh or any other key to continue.
Shell>
You will see the mapping table shown here. These are the filesystems the EFI system is aware of; in this case FS0: is our boot partition. Just type fs0: to switch into that filesystem.
FS0:\> ls
Directory of: FS0:\
10/31/2025 13:42 9,058 background.png
01/05/2026 18:41 2,392,064 converted-font.pf2
11/26/2024 12:55 <DIR> 512 EFI
11/26/2024 15:02 <DIR> 512 grub
10/31/2025 13:37 <DIR> 1,024 kernels
2 File(s) 2,401,122 bytes
3 Dir(s)
FS0:\>
There are some Linux-y commands you can run inside the EFI shell, like ls to list a directory. We need to find grub.cfg — this is most likely in the grub folder. On other systems this may be under a folder named after the OS, just use ls and cd to browse around until you see it.
FS0:\> ls grub
Directory of: FS0:\grub\
11/26/2024 12:55 <DIR> 512 .
11/26/2024 12:55 <DIR> 0 ..
11/26/2024 12:55 <DIR> 16,384 arm64-efi
11/26/2024 12:55 <DIR> 512 fonts
11/26/2024 15:02 2,325 grub.cfg
11/26/2024 12:55 1,024 grubenv
11/26/2024 12:55 <DIR> 3,072 locale
11/26/2024 12:55 55 state
3 File(s) 3,404 bytes
5 Dir(s)
FS0:\>
Once you have located grub.cfg, we need to rename it. This will cause GRUB to fail and drop us to the GRUB prompt. The rename can be done with mv.
FS0:\> cd grub
FS0:\grub\> mv grub.cfg grub.cfg-bak
Moving FS0:\grub\grub.cfg -> FS0:\grub\grub.cfg-bak
FS0:\grub\> ls
11/26/2024 12:55 <DIR> 1,024 .
11/26/2024 12:55 <DIR> 0 ..
11/26/2024 12:55 <DIR> 16,384 arm64-efi
11/26/2024 12:55 <DIR> 512 fonts
11/26/2024 15:02 2,325 grub.cfg-bak
11/26/2024 12:55 1,024 grubenv
11/26/2024 12:55 <DIR> 3,072 locale
11/26/2024 12:55 55 state
3 File(s) 3,404 bytes
5 Dir(s)
Now we can go ahead and boot into GRUB. You will need to find the EFI file for this; it will have a slightly different name for different systems. On this VM, it’s under /EFI/BOOT.
FS0:\grub\> cd ..
FS0:\> ls
Directory of: FS0:\
10/31/2025 13:42 9,058 background.png
01/05/2026 18:41 2,392,064 converted-font.pf2
11/26/2024 12:55 <DIR> 512 EFI
01/29/2026 18:39 <DIR> 1,024 grub
10/31/2025 13:37 <DIR> 1,024 kernels
2 File(s) 2,401,122 bytes
3 Dir(s)
FS0:\> cd EFI
FS0:\EFI\> ls
Directory of: FS0:\EFI\
11/26/2024 12:55 <DIR> 512 .
11/26/2024 12:55 <DIR> 0 ..
11/26/2024 12:55 <DIR> 512 BOOT
0 File(s) 0 bytes
3 Dir(s)
FS0:\EFI\> cd BOOT
FS0:\EFI\BOOT\> ls
Directory of: FS0:\EFI\BOOT\
11/26/2024 12:55 <DIR> 512 .
11/26/2024 12:55 <DIR> 512 ..
11/26/2024 12:55 147,456 BOOTAA64.EFI
FS0:\EFI\BOOT\>
As this is an ARM64 server, I only have a BOOTAA64.EFI file. Just type the name of the file and hit Enter. It won’t look like much has happened, but GRUB should have loaded.
You can type clear to clear the screen to make it easier to see.
grub>
That’s a mighty fine looking console right there, right?
Booting… Anything#
Now we have a grub prompt, we need to tell it what to do. First, we need to identify the boot partition(s).
grub> ls
(hd0) (hd0,gpt15) (hd0,gpt1) (hd1)
grub> ls (hd0,gpt
Possible partitions are:
Partition hd0,gpt1: Filesystem type ext* - Label `cloudimg-rootfs' -
Last modification time 2026-01-04 20:36:36 Sunday, UUID
d4b85d57-37ed-4049-aa67-c5a7415a1974 - Partition start at 103424KiB - Total
size 157182959.5KiB
Partition hd0,gpt15: Filesystem type fat - Label `UEFI', UUID 3000-6631
- Partition start at 1024KiB - Total size 101376.5KiB
grub> ls (hd0,gpt1
Type ls to list the available partitions; the Tab key is your friend here and will try to autocomplete the partition names. It will also show the available partitions and some details. Here, we can see the root filesystem is (hd0,gpt1) and (hd0,gpt15) is the EFI partition.
grub> ls (hd0,gpt15)/
grub/ converted-font.pf2 background.png kernels/ efi/
grub>
You can ls the files from the partitions directly and check what is in there. This partition is the boot partition, so the EFI files are in here along with the kernels. This is what we’re interested in.
Next, set this partition as the root for GRUB and identify the kernel files to load. This folder layout may be different on other systems; have a poke around and try the /boot folder on your root partition too.
grub> set root=(hd0,gpt15)
grub> ls (hd0,gpt15)/kernels/
nfq6y74sqmdvh4vhn0lxj7dm2g3aiyzg-linux-6.6.94-Image.tmp
9b3yq6x6h5nx5v35g631yfg3fv73flz3-linux-6.1.69-Image
jal3vsqwci9w7d9ra7n6ld7klswgk5dd-initrd-linux-6.1.69-initrd
I have a Linux 6.1 kernel and initrd available here, perfect. Grub can be pointed at these.
grub> ls (hd0,gpt15)/kernels/
nfq6y74sqmdvh4vhn0lxj7dm2g3aiyzg-linux-6.6.94-Image.tmp
9b3yq6x6h5nx5v35g631yfg3fv73flz3-linux-6.1.69-Image
jal3vsqwci9w7d9ra7n6ld7klswgk5dd-initrd-linux-6.1.69-initrd
grub> linux /kernels/9b3yq6x6h5nx5v35g631yfg3fv73flz3-linux-6.1.69-Image rw init=/bin/sh
grub> initrd /kernels/jal3vsqwci9w7d9ra7n6ld7klswgk5dd-initrd-linux-6.1.69-initrd
The Linux kernel is specified by the linux command followed by rw init=/bin/sh — you can also use /bin/bash here.
The initrd command specifies the initial ramdisk. This is the tiny OS I mentioned earlier that handles the first part of the Linux boot.
We’re now ready to boot. By adding the extra parameters to the linux command, we will boot into a bash/sh prompt with root privileges and be able to perform the surgery. Just type boot in the grub prompt to set it going.
If all is done correctly, you should be dropped to a root shell. If not, check all the previous steps by rebooting and trying again, your VM will boot straight to the grub prompt if you restart from here.
If you end up in initramfs, the process will be similar to this, but you will need to find and mount your root partition first.
Starting device mapper and LVM...
[ 11.769774] random: crng init done
checking /dev/sda1...
fsck (busybox 1.36.1)
[fsck.ext4 (1) -- /mnt-root/] fsck.ext4 -a /dev/sda1
cloudimg-rootfs: recovering journal
cloudimg-rootfs: clean, 537782/19449600 files, 9460396/39295739 blocks
mounting /dev/sda1 on /...
[ 11.876884] EXT4-fs (sda1): mounted filesystem with ordered data mode. Quota mode: none.
[ 11.882239] EXT4-fs (sda1): re-mounted. Quota mode: none.
sh: cannot set terminal process group (-1): Inappropriate ioctl for device
sh: no job control in this shell
sh-5.2#
Now we have access, we need to get the SSH key into authorized_keys. Of course, replace “SSH KEY” with your public key (still in quotes).
sh-5.2# cd /root/.ssh/
sh-5.2# echo "SSH KEY" >> authorized_keys
sh-5.2#
The availability of tools in this shell will be limited, My console doesn’t even have ls or mv available. The help command will give you an idea of what you’re working with.
If you got this far and don’t know your way around the Linux terminal, damn, nice going! But seriously, you might need to do some Googling/AI’ing here to help you figure out how to get your key in.
Since this is a cloud server, I’d strongly suggest not setting a password and keeping the server set to key-based authentication only. This greatly reduces the attack surface of a public-facing server. Have a look at your auth.log some day if you want a laugh/scare, especially if you have port 22 open to the internet.
Unbreaking Grub#
Now the SSH key is inserted, we can reboot and go back to the EFI console to undo what we did earlier.
FS0:\grub\> ls
Directory of: FS0:\grub\
11/26/2024 12:55 <DIR> 1,024 .
11/26/2024 12:55 <DIR> 0 ..
11/26/2024 12:55 <DIR> 16,384 arm64-efi
11/26/2024 12:55 <DIR> 512 fonts
11/26/2024 15:02 2,325 grub.cfg-bak
11/26/2024 12:55 1,024 grubenv
11/26/2024 12:55 <DIR> 3,072 locale
11/26/2024 12:55 55 state
3 File(s) 3,404 bytes
5 Dir(s)
FS0:\grub\> mv grub.cfg-bak grub.cfg
Moving FS0:\grub\grub.cfg-bak -> FS0:\grub\grub.cfg
- [ok]
FS0:\grub\>
You can exit the EFI console or boot the same way as before by browsing to the EFI file. If everything went well, your VM should boot up and you should have SSH access once again.
Congratulations, you have successfully broken back into your own server! Now go and break it again… I dare you.