Getting Old Games to Work: Age of Mythology under Virtualbox (mac OS 11.15)

Here's a short little write up on getting Age of Mythology (great break down by staff here )running under Virtualbox. I'm running mac OS 10.15.4 but this should work under all platforms using VirtualBox.

Afteri some trial and error, I  found that VirtualBox 6.1 wasn't working with 3D acceleration under XP (apparently it's been removed) so I had to downgrade to 5.2. 

We'll need DirectX support for games anyway, so download that here. You'll know if DirectX isn't working because you'll get a blank black screen with sound, but no controls.

Boot XP in Safe Mode (Hold F8), since the Guest Additions only allows DIrectX support  to be installed under safe mode . 


Once I got Age of Mythology installed, the first error that came up was an unsupported graphics card:

This graphics card is not supported by Age of Mythology.
Please check for a full list of supported graphics cards.
Age of Mythology will now exit.

Video Card 0: VBoxDisp.dll VirtualBox Graphics Adapter Vendor(0x80EE) Device(0xBEEF)

Makes sense since it's a virtual adapter and the game has no clue what the hell it is. So we have to add the adapter to the available list!

Following this post on Stack Exchange, our vendor is 0x80EE.

So we create a graphics adapter file in 

C:\Program Files\Microsoft Games\Age of Mythology\gfxconfig 

C:\Program Files\Microsoft Games\Age of Mythology\gfxconfig2

We're going to name it 0x80EE_vmware.gfx





After that, you'll want to start the game in a safe mode, with low graphics. Create a shortcut to AOM and add these arguments:

"C:\Program Files\Microsoft Games\Age of Mythology\aomx.exe" xres=800 +noIntroCinematics bpp=16 +window +lowend +terrainHalfDensity +lowPoly -waterbump skipMipMapLevels=1 graphicDetail=2

And voila! That's pretty much it. It's very convenient having portable virtual machines as well, especially for old school games. I'm glad there's still people trying to get these older games working, as some of the forum posts are from as new as 2019. The game itself was released in 2003!


Linux 4.15.2 on SnackLinux

Quick update to SnackLinux, rolled out Linux 4.15.2 with Busybox 1.28.0. Also switched over to x86_64 only (for now at least) since it simplifies a lot of things. I removed the need to staticly link everything and get rid of that niche, since a few other smaller distros cover that (Alpine Linux for example). Again, this simplifies building packages and running into less issues. Check it out on Github for build instructions, or the getting started page on getting SnackLinux running.

Why is my Android keyboard sending unencrypted data? And where?

I recently switched phones from a LG G4 to a Sony Xperia X Performance (isn't that a mouthful!), so I had a spare Android phone lying around. What better use for than to sniff some traffic!


First up is ai.type Free Emoji Keyboard, a free keyboard that has built in text-swiping features (Google Play), emoji, a calculator and much more. Android keyboards have the ability to read what you type, mostly just to improve text correction and prediction. This is an understandable permission but has the opportunity for misuse.

I loaded up Burp Suite, added the proxy setting to the phone and off I went. First request right off:

Holy leaking information batman! The app sent my email addresses, latitude, longitude, IP address and device info to their server over HTTP.

Now you may be thinking "well ads display in apps too, they must send juicy tracking information back too", so let's take a look.

Here's a request after opening up Don't Tap The White Tile:

GET /v2/config.json?u-id-key=82549130&u-key-ver=1&h-user-agent=Mozilla%2F5.0+%28Linux%3B+Android+6.0%3B+LG-H812+Build%2FMRA58K%3B+wv%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Version%2F4.0+Chrome%2F53.0.2785.124+Mobile+Safari%2F537.36&tz=-25200000&u-appbid=com.umonistudio.tile&mk-version=pr-SAND-ETFTH-20151009&u-appver= HTTP/1.1
User-Agent: Mozilla/5.0 (Linux; Android 6.0; LG-H812 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.124 Mobile Safari/537.36
If-Modified-Since: Thu, 29 Sep 2016 17:31:14 GMT
Connection: close
Accept-Encoding: gzip

And the response is nothing spectacular:

HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Mon, 03 Oct 2016 05:32:46 GMT
Vary: Accept-Encoding
Content-Length: 1867
Connection: keep-alive

    "common": {
        "ids": {
            "O1": true,
            "SO1": true,
            "IX": true,
            "SID": true,
            "LID": true,
            "UDID": true,
            "UM5": true,
            "IDA": true,
            "IDV": true,
            "FBA": true,
            "GID": true,
            "LTVID": true,
            "si": 60,
            "srt": 3,
            "shs": 50,
            "e": true,
            "sced": true,
            "scoe": true,
            "scvw": false,
            "ep": "",
            "mr": 3,
            "ri": 3,
            "wfd": 0,
            "cof": 0,
            "ao": false,
            "aoep": "",
            "aoi": 86400,
            "as": true,
            "assi": 180,
        "metric": {
            "url": "",
            "nri": 5,
            "max": 1000,
            "sf": 50,
            "dt": 10
        "endpoints": {
            "house": "",
            "rules": "",
            "events": ""
        "mdb": 1000,
        "elim": 100,
        "pint": 30,
        "mkey": 20,
        "mval": 50,
        "plim": 100,
        "mec": 5,
        "aidl": 100,
        "mr": 5,
        "at": {
            "session": false,
            "purchase": false,
        "ltvid": 0
    "IOS": {
    "AND": {


At least it's over HTTPS and just has non-personal identifiable information.

Here's another example call from Vertigo Racing via Flurry analytics:

Again, over SSL and just sending back phone data information.


Back to our target, I found that  the keyboard would instantly generate a HTTP request after I'm done typing. This sounds safe!

It sends a GET request to with information that identifies what app was in use, date, and some weird long string, see below for the request.

GET /aggregationserver/agreggate?d_ins_id=dzi389isCLU&fl=en_KZ&cal_dow=2&cal_doy=277&cal_y=2016&cal_dom=3&l=en&t=rVjlC57xipA0sUVkCk49twiILhTcPv42C2DNGgrZ0cxilyN1QTONhIW2%2BHkiimGXcDkiH2f6Klzt%0AGlBi3gqmTOIZhRD%2B%2Fyi8TCmCnVB%2FPak%3D%0A&c=CA&p=com.lge.qmemoplus&cal_hod=0&d=c65c7885266ec9e3&cal_moh=30&cal_woy=41&aid=8775b9ea-80ed-42a8-ba29-597bd896c452& HTTP/1.1
User-Agent: Dalvik/2.1.0 (Linux; U; Android 6.0; LG-H812 Build/MRA58K)
Connection: close
Accept-Encoding: gzip

I haven't been able to decode the string yet, so I haven't been able to come down to what it is sending but one can only imagine. The data it is sending is base64 encoded, possibly padded with some other device information. I'll update the post if I get any further. It is possible that the information it sends back is encrypted and "helps improve" the text prediction service. Even so, why must it send that information back? Why can the app not improve a user's predictive text on the device itself? I tested a few other keyboards ( Minuum, Google Keyboard, KitKat Keyboard, Chrooma Keyboard) and nothing was sent. 

But wait there's more! The keyboard will periodically want to send some large binary data to

93 kilobytes of data? Not suspicious at all. I've uploaded the above data if anyone else would like to poke around in it, see the attachments at the bottom of this post.

It is quite scary when an app sends this type of information over plain text, considering how much one trusts their mobile device.


Create an ad blocking VPN for mobile use

Ads on your mobile device can use a fair chunk of your monthly data, so I set out to create a way to disable them while browsing. If you happen to have a rooted Android phone, AdAway is the easiest solution that I have found. Another solution is creating your own DNS server, to have all traffic to unwanted domains be unresolved. While that's great, one can only set the DNS servers manually for wifi, not for mobile data (3G/LTE). The fix this issue, what I did was create an OpenVPN server with Bind9 zones to block unwanted domains.


To bootstrap installing VPN, I used this script to quickly set up an OpenVPN server. Its all very straight forward and will prompt for a few options.

If all went well, it will generate a client profile for you to use (.ovpn). Next, I installed bind9 and followed this tutorial. Once that is all set up, you'll have an ad blocking DNS server! Only thing left to do is force clients to use the DNS.

Add this to /etc/openvpn/server.conf

push "dhcp-option DNS your.server.ip.address"
push "register-dns"

That way it'll push the DNS server to your clients that connect. I don't have an data on how much mobile data ad blocking will save, but even just loading websites is quicker now.

Base64 image uploading to Pasteros

Pasteros now has JPEG handling abilities when pastes are uploaded with base64 data (see commit). Convert your image to base64 like this and view it by add .jpg to the end of the paste ! I also wrote a command line utility that uploads a selected screenshot area to Pasteros.



function uploadImage {
 content=$(cat $1 | base64 -w 0)
 pasteid=$(curl -silent -H "Expect:" -X POST -d $content | tail -1)
 echo "$pasteid.jpg"  | xclip -selection c

DISPLAY=:0.0 scrot -s "shot.jpg" 
uploadImage "shot.jpg"
rm "shot.jpg"
notify-send "Done"



Remote code execution with Hitron CGNM-2250

Edit: This has been fixed in the latest firmare update

The routers that you receive from your ISP are almost always garbage: not many options to configure and pitiful wifi range. The router/modem that Shaw customers receive is the Hitron CGNM-2250 thankfully isn't completely terrible, 802.11ac plus gigabit ports. I was poking about and researching the model and came upon an exploit for a similar model version for remote code execution. The CGNM-2250 is vulnerable as well, for reference my software version is with hardware version 1A. The input for the ping utility through the web interface isn't sanitized so you can enter arbitrary input. I discovered that it has a few basic utilities, including Dropbear.


From there you can ssh into your router with

ssh mso@ -p 29

The password is msopassword (go figure!)

You'll be greeted with a fun menu driven interface. 

Most of the menus are for debugging and initial programming purposes. There is a menu option to set the TFTP download image URL to flash the device, so it might be possible to flash your own firmware though I don't know how possible that is.

Going back to the web interface, I was able to see what tools and programs are available on the router.

It is your basic Linux system, busybox plus some manufacturer utilities. For a full list of binaries available see below: #/sbin wlanconfig wifitool wifirrm watchdog vconfig utelnetd udhcpd udhcpc udevstart udevsend udevd udev ti_udhcpc ti_todc ti_tftp ti_syslogd ti_dhcp6c start-stop-daemon route rmmod reboot radartool poweroff pktlogdump pktlogconf manufacture_ath_throughput_sta manufacture_ath_throughput_ap makeVAP lsmod klogd killVAP iwpriv iwlist iwconfig insmod init ifconfig ht_wifi_ioctl ht_atom_cmd hostapd_cli hostapd halt fsck.ext3 fdisk cfg blockdev atomcmdlist athstatsclr athstats athcfg_api arp apup apstats apdown apcfg activateVAP 80211stats #/usr/bin which uptime traceroute6 traceroute tr top tftp test renice pstree pmap mkfifo logger killall hexdump head free flock find expr dirname cut crontab basename awk add-shell [[ [ #/bin xmlwf umount touch tar sync sleep sh sed rm pwd ps ping6 ping pcap-config netstat mv mount more mknod mkdir luac lua ls login ln kill iptunnel iprule iproute iplink ipaddr ip hostname grep getopt false echo dnsdomainname dmesg df dd date cp clnkstat clnkrst clnkqos clnkpm clnkmocamib clnkmem clnkmcast clnkhwtst clnkfwupd clnkcfg clinkd chown chmod cat busybox bash ash DCAP.46 DCAP.45 DCAP.44 DCAP.42 DCAP.41 DCAP.40 DCAP.38 DCAP.37 DCAP.35 DCAP.19 DCAP.18 DCAP.16 DCAP.137 DCAP.132 DCAP.126 DCAP.125 DCAP.123 DCAP.122 DCAP.116 DCAP.115 DCAP.112 DCAP.111 DCAP.110 DCAP.109 DCAP.107 DCAP.104 DCAP.103 DCAP.102 DCAP.101 DCAP.08 DCAP.03 DCAP.02 DCAP CandDdvr.ko ##/usr/sbin watchdog_rt upstream_manager_1q upstream_manager upgradebox update testmode sync_app_np_reboot swdl2 sw_dl snmpcmd snmp_agent_cm setstartup setkey setenv sched runall rpc_reverse_server_util rpc_reverse_server rpc_management_server rpc_ifconfig rfs.cfg rfs regs qos_dsx_sm productionmode printenv pp_fw_download portmap pmap_set pmap_dump pcd nvtst nvread mptint mlx logger ledcfg l2switch_iram.img l2switch_init l2switch_dram.img iostat iccctl icc_genEvent htxvendordb ht_iwcmd ht_buttond hal_tuner_mgr hal_event_mbox hal_cmd_mbox gptimer gim getenv flash_eraseall fdump ext_switch_init eventmgr_cm energy_manager_app downstream_manager docsis_mac_manager docsis_mac_driver docsis_init_once docsis_dl_box docsis_config_to_text dmg_provisioning dl dispatcher ddl dbridge_init crond cmdl cm_status cli chroot cefdk cc_init_once brctl bpi_tek bpi_sa_map bpi_auth MxL_HRCLS_FW_4.1.5.5.mbin MxL_HRCLS_FW.mbin FwUpstreamDocsis3_I.bin FwUpstreamDocsis3_D.bin FwUpstreamDocsis2_I.bin FwUpstreamDocsis2_D.bin Through digging in the CLI menu I found that /etc/scripts/ is ran on startup, making it a easier to inject any commands you want to run at startup. I haven't been able to get to a shell yet, since logging in with the 'mso' user it takes you to the CLI menu. But cat /etc/passwd reveals:


One should be able to symlink /bin/sh to /usr/sbin/cli so upon login it would drop to a shell. Entering commands via the web interface is a bit tricky since it doesn't like pipes (|), it's just a matter of getting around the JS validation. Once I can get to a shell I'll write a followup to this post Issue patched in version


SnackLinux update

SnackLinux now has a whopping 31 packages, which include a (somewhat) working gcc toolchain and other fun GNU utilities (vitetris included!). I was able to add Docker support as well, which is just tarballed userland. Getting a working gcc toolchain was a bit of a pain in the butt. Pain in the butt because musl doesn't play nice with every piece of software out there, since most (that I have encountered anyway) think we're using glibc. And since SnackLinux is focused on every packaging being staticly compiled, not every package plays nice with that either. For example, there is a Python 2.7.9 package in the repository that is missing quite a few modules, see below:

_codecs_cn         _codecs_hk         _codecs_iso2022 
 _codecs_jp         _codecs_kr         _codecs_tw      
 _ctypes            _ctypes_test       _curses         
 _curses_panel      _hashlib           _hotshot        
 _json              _lsprof            _multibytecodec 
 _multiprocessing   audioop            bz2                
 dl                 future_builtins 
 imageop            linuxaudiodev      ossaudiodev     
 readline           syslog      

Fortunately, it works. Sort of. I wouldn't recommend using it at all. This is why gcc 4.9.2 needs to be patched for musl to get around this. That being said, most of the current packages are natively compiled on SnackLinux itself, so at least it's somewhat self sufficient ;)


I just got Nim working, as well as Lua. Perl is possible through staticperl. It's suprising the amount of software that depends on Perl, pkg-config and syslinux come to mind. With a working Python implementation being the last holdout, SnackLinux may finally gain some whichever niche it fits in... I suppose.