Skip to content

Keeping a Jailbroken PS4 Offline From Sony — Properly

8/10/2026Cybersecurity & Quality Assurance19 min read

I bought a jailbroken PS4. The exploit it runs is tied to the firmware version it shipped with, and there is no downgrade path on this hardware. 

So the console needs to be online — but never that kind of online.

The internet is full of guides for this. Almost all of them say the same thing: paste a hosts list into your router, done. I did that, then spent an hour trying to break my own setup, and broke it three different ways in about ten minutes. This post is mostly about those ten minutes, because that's the part the guides skip.

The end state: a dedicated router that the PS4 plugs into, with three independent blocking layers, every alternative DNS path closed, and a design that fails closed — if something breaks, the console loses DNS entirely rather than quietly falling through to an open path.


The hardware

I had a Roltek RLTK420 lying around — an industrial 4G/LTE VPN router from Roltek Technology, the previous generation of their current RLTK421 Industrial 4G/LTE VPN Router. It's marketed for machine-to-machine work — RS485 serial, Modbus RTU-to-TCP conversion, PLC remote access, hardware watchdog for 24/7 operation — which is a funny pedigree for a games console babysitter, but it means the thing is built to sit in a cabinet and not fall over.

The datasheet describes it as "Linux-based with custom configuration interface." That undersells it. SSH in and it's OpenWrt:

Hostname       RoltekLTE
SoC            MediaTek MT7628AN (2.4 GHz only, 802.11b/g/n)
Kernel         Linux 5.4.81, mipsel_24kc
Distribution   OpenWrt SNAPSHOT r15149-28a9ac74cc
Target         ramips/mt76x8
Storage        /dev/mtdblock6, jffs2 overlay, 6.1 MB (5.7 MB free)
LAN            192.168.2.1/24
WAN as shipped LTE via QMI (/dev/cdc-wdm0), APN "internet"

The vendor layer sits on top as a handful of shell scripts in /usr/roltek/ plus some cron entries — that's the "custom configuration interface," and it's also where the watchdog from the spec sheet lives. /etc/config/mbusd was present too, which is the Modbus daemon the datasheet advertises. None of it gets in the way; standard uci works normally.

Anything running OpenWrt works for this. You need dnsmasq (standard) and iptables/fw3 (standard on 19.07-era builds). You do not need ipset — mine was compiled without it (no-ipset in the dnsmasq build flags), which ruled out the elegant "dnsmasq populates an ipset" approach and forced a slightly more manual solution later.

Check yours:

dnsmasq --version | head -3
# look for "no-ipset" vs "ipset" in the compile options

The plan: PS4 → router LAN port → router joins my Wi-Fi as a client → internet. The router sits in the middle of every packet the console sends.


Step 1: getting the router online

The router had no Wi-Fi configured at all — /etc/config/wireless had a wifi-device section but no wifi-iface, so iw dev returned nothing. The radio itself was fine.

Client ("station") mode setup:

uci set network.wwan=interface
uci set network.wwan.proto='dhcp'

uci set wireless.radio0.channel='auto'      # let the client follow the AP
uci set wireless.wwan=wifi-iface
uci set wireless.wwan.device='radio0'
uci set wireless.wwan.mode='sta'
uci set wireless.wwan.network='wwan'
uci set wireless.wwan.ssid='YOUR SSID'
uci set wireless.wwan.encryption='psk2'
uci set wireless.wwan.key='YOUR KEY'

uci add_list firewall.@zone[1].network='wwan'   # zone[1] is 'wan' on stock config

uci commit network; uci commit wireless; uci commit firewall
wifi reload

Check which zone index is actually wan before you copy that blindly:

uci show firewall | grep -E "zone.*name"

A useful failure

The first network I tried never associated. The logs are worth reading carefully, because they tell you which thing is wrong:

wlan0: send auth to 98:25:4a:20:c0:d8 (try 1/3 … 3/3)
wlan0: authentication with 98:25:4a:20:c0:d8 timed out
wlan0: associate with 98:25:4a:20:c0:d8 (try 1/3 … 3/3)
wlan0: association with 98:25:4a:20:c0:d8 timed out
CTRL-EVENT-SSID-TEMP-DISABLED auth_failures=4 reason=CONN_FAILED

This is not a wrong-password failure. A bad passphrase fails during the WPA2 four-way handshake, and you'd see something like WPA: 4-Way Handshake failed. Timeouts at the 802.11 authentication and association stages mean your frames aren't being acknowledged — the link is too weak.

A scan confirmed it: a single AP for that SSID at −89 dBm, basically the noise floor.

iw dev wlan0 scan | awk '/^BSS/{b=$2} /signal:/{s=$2} /SSID:/{print s"  "b"  "$2}' | sort -rn

Switching to a network at −66 dBm associated on the first attempt:

WPA: Key negotiation completed with ba:ad:80:b4:f3:f1 [PTK=CCMP GTK=CCMP]
udhcpc: lease of 10.67.64.128 obtained, lease time 3599

Rule of thumb for this radio: below about −85 dBm it won't associate at all.

While you're here: kill anything you don't need

The LTE modem couldn't register on my SIM and was retrying in a loop:

uci set network.LTE.auto='0'
uci set network.LTE.disabled='1'
uci commit network

One gotcha — my router's vendor ships a cron job that runs every minute:

# /usr/roltek/checkInet
if ping -c 1 -W 5 $host ; then
  echo "1" > /sys/class/leds/internet/brightness
else
  echo "0" > /sys/class/leds/internet/brightness
  ifdown LTE ; killall uqmi ; ifup LTE     # <-- resurrects LTE
fi

Setting disabled='1' on the interface makes netifd refuse the ifup, so the vendor's own watchdog can't undo it. Worth checking crontab -l on any consumer router before you assume your config will stick.


Step 2: the DNS blocklist (where most guides stop)

The list I used is the well-known PS4 update blocklist credited to depressive_monk (https://www.reddit.com/user/depressive_monk/) — 67 hostnames covering the regional update servers (d/f/h + country code + 01.ps4.update.playstation.net), the CDN endpoints, and the telemetry APIs.

The important move is putting it on the router, not on the console. A hosts file on one machine covers one machine. On the router it covers everything on the LAN.

OpenWrt's dnsmasq takes a hosts-format file directly via addnhosts:

# /etc/ps4block.domains  = one hostname per line, the source of truth
# /etc/ps4block.hosts    = generated, what dnsmasq actually reads

uci add_list dhcp.@dnsmasq[0].addnhosts='/etc/ps4block.hosts'
uci commit dhcp
/etc/init.d/dnsmasq restart

Generate both address families. Most published lists are IPv4 only. If you only write 0.0.0.0 hostname, an AAAA query returns NODATA — which usually works out, but it's a gap you don't need to leave. I generate two records per name:

0.0.0.0    dau01.ps4.update.playstation.net
::    dau01.ps4.update.playstation.net

67 hostnames → 134 records. Confirm dnsmasq actually loaded them:

logread | grep ps4block
# dnsmasq[3579]: read /etc/ps4block.hosts - 134 addresses

At this point every guide I found says "done". It isn't.


Step 3: breaking my own setup

Three attacks, run from a machine on the LAN. All three worked.

Attack 1 — DNS over HTTPS

DoH rides inside ordinary port 443 traffic. dnsmasq never sees the query.

Invoke-RestMethod -Uri "https://cloudflare-dns.com/dns-query?name=dau01.ps4.update.playstation.net&type=A" `
                  -Headers @{accept='application/dns-json'}
23.50.131.198, 23.50.131.222real addresses, block bypassed

Attack 2 — DNS over TLS

Port 853, wide open outbound:

Test-NetConnection 1.1.1.1 -Port 853
# TcpTestSucceeded : True

Attack 3 — no DNS at all

This is the one that matters. Take an IP from attack 1 and connect straight to it, supplying the hostname in the Host: header:

$req = [Net.HttpWebRequest]::Create("http://23.50.131.198/")
$req.Host = "dau01.ps4.update.playstation.net"
$req.GetResponse()
404 Not Found     Server: AkamaiNetStorage

404 because I requested /, but the point stands: the server answered. A DNS blocklist does nothing against a client that already knows the address.

The one thing that was already fine

Manually setting the console's DNS to 8.8.8.8 — the classic bypass, and the first thing anyone tries on a PS4 — was already covered, because I'd added a redirect rule. More on that next.


Step 4: closing the escapes

The goal is to make DNS the single mandatory chokepoint, then make sure nothing can route around it.

# 1. Every port 53 query on the LAN gets redirected to the router,
#    no matter what DNS server the client was configured with.
uci add firewall redirect
uci set firewall.@redirect[-1].name='Force-LAN-DNS'
uci set firewall.@redirect[-1].src='lan'
uci set firewall.@redirect[-1].proto='tcp udp'
uci set firewall.@redirect[-1].src_dport='53'
uci set firewall.@redirect[-1].dest_port='53'
uci set firewall.@redirect[-1].dest_ip='192.168.2.1'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].family='ipv4'

# 2. Safety net: anything on port 53 that somehow escapes the DNAT gets rejected.
uci add firewall rule
uci set firewall.@rule[-1].name='Block-Stray-DNS'
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].dest='wan'
uci set firewall.@rule[-1].proto='tcp udp'
uci set firewall.@rule[-1].dest_port='53'
uci set firewall.@rule[-1].target='REJECT'

# 3. Kill DNS over TLS.
uci add firewall rule
uci set firewall.@rule[-1].name='Block-DoT-853'
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].dest='wan'
uci set firewall.@rule[-1].proto='tcp udp'
uci set firewall.@rule[-1].dest_port='853'
uci set firewall.@rule[-1].target='REJECT'

# 4. Kill DoH at the known public resolvers (port 443, by address only).
uci add firewall rule
uci set firewall.@rule[-1].name='Block-DoH-Resolvers'
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].dest='wan'
uci set firewall.@rule[-1].proto='tcp udp'
uci set firewall.@rule[-1].dest_port='443'
uci set firewall.@rule[-1].target='REJECT'
for ip in 1.1.1.1 1.0.0.1 1.1.1.2 1.0.0.2 1.1.1.3 1.0.0.3 \
          104.16.248.249 104.16.249.249 162.159.36.1 162.159.46.1 \
          8.8.8.8 8.8.4.4 \
          9.9.9.9 9.9.9.10 9.9.9.11 149.112.112.112 149.112.112.9 \
          94.140.14.14 94.140.15.15 94.140.14.15 94.140.15.16 \
          208.67.222.222 208.67.220.220 146.112.41.2 \
          45.90.28.0/24 45.90.30.0/24 \
          185.222.222.222 45.11.45.11 194.242.2.2 76.76.2.0/24 76.76.10.0/24; do
  uci add_list firewall.@rule[-1].dest_ip="$ip"
done

uci commit firewall
/etc/init.d/firewall restart

And remove the parallel IPv6 stack entirely, so you don't have to filter it twice:

uci set dhcp.lan.ra='disabled'
uci set dhcp.lan.dhcpv6='disabled'
uci commit dhcp

Note that blocking DoH by IP is inherently a partial measure. DoH can run on any HTTPS host; that list covers the major public providers, not the concept. It's defence in depth, not a guarantee. What makes it sufficient here is that a PS4 doesn't implement DoH at all — the rule exists to stop anything else that ends up on this LAN.

The nice property of the DNAT approach: it fails closed. If dnsmasq dies, port 53 traffic is still being redirected to a router that isn't answering, so clients get nothing at all. A fault tightens the block instead of opening it.


Step 5: the direct-IP problem, and the Akamai trap

Attack 3 is still open. Fixing it means blocking the addresses themselves.

The obvious move is to block the whole range. Don't. Here's what those hostnames actually resolve to:

2.19.x  2.20.x  2.21.x     Akamai
23.x    (several /16s)     Akamai — includes 23.50.131.198, "AkamaiNetStorage"
92.122.x  95.100.x         Akamai
184.24.x  184.26.x         Akamai
52.40.62.x                 AWS us-west-2 (Sony API)

Akamai edge nodes are shared infrastructure serving a large fraction of the web. Block 23.0.0.0/8 and you'll take out unrelated services along with Sony. Block only the specific addresses you resolved, as /32s.

But the addresses move

This is the part that surprised me. Resolving the same 67 hostnames repeatedly gives you a different answer set almost every time:

initial probe          44 unique IPs
next run               20 unique IPs   ← barely overlapping the first 44
merged + one round     82 unique IPs
after another round   105 unique IPs

A static snapshot goes stale within days. So the list has to be cumulative and self-refreshing: re-resolve on a schedule, union with what you already have, reload.

Since ipset wasn't available, I used a dedicated iptables chain, hooked into the head of FORWARD so it severs established connections too:

#!/bin/sh
# /etc/ps4block.fwinclude — registered as an fw3 include so it re-runs on every
# firewall start/reload (and therefore on every boot)
IPS=/etc/ps4block.ips

iptables -N PS4BLOCK 2>/dev/null
iptables -F PS4BLOCK

n=0
if [ -f "$IPS" ]; then
  while read -r ip; do
    case "$ip" in ''|\#*) continue ;; esac
    iptables -A PS4BLOCK -d "$ip" -j REJECT --reject-with icmp-admin-prohibited && n=$((n+1))
  done < "$IPS"
fi

iptables -C FORWARD -j PS4BLOCK 2>/dev/null || iptables -I FORWARD 1 -j PS4BLOCK
logger -t ps4block "PS4BLOCK loaded: $n IPs"

Register it so it survives:

uci add firewall include
uci set firewall.@include[-1].path='/etc/ps4block.fwinclude'
uci set firewall.@include[-1].reload='1'
uci commit firewall

The refresher — note it queries upstream resolvers directly, because asking our own dnsmasq would just return 0.0.0.0:

#!/bin/sh
# /etc/ps4block.ipsync   [--reset]
SRC=/etc/ps4block.domains
OUT=/etc/ps4block.ips
TMP=/tmp/ps4ipsync.$$

UP=$(sed -n 's/^nameserver //p' /tmp/resolv.conf.d/resolv.conf.auto 2>/dev/null | head -2)
SERVERS="$UP 8.8.8.8 1.1.1.1"

: > "$TMP"
while read -r d; do
  d=$(echo "$d" | sed 's/#.*//' | tr -d ' \t\r')
  [ -n "$d" ] || continue
  for s in $SERVERS; do
    nslookup "$d" "$s" 2>/dev/null | sed -n 's/^Address[ 0-9]*: *//p' \
      | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' >> "$TMP"
  done
done < "$SRC"

for s in $SERVERS; do grep -vx "$s" "$TMP" > "$TMP.x" && mv "$TMP.x" "$TMP"; done
grep -vE '^(0\.0\.0\.0|127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)' "$TMP" \
  | sort -u > "$TMP.f"

COUNT=$(grep -c . "$TMP.f")
if [ "$COUNT" -lt 5 ]; then
  logger -t ps4block "only $COUNT IPs resolved — keeping existing list"
  rm -f "$TMP" "$TMP.f"; exit 1              # fail closed, never wipe the list
fi

[ "$1" != "--reset" ] && [ -f "$OUT" ] && cat "$OUT" >> "$TMP.f"
grep -E '^[0-9]' "$TMP.f" | sort -u > "$OUT"
rm -f "$TMP" "$TMP.f"
/etc/ps4block.fwinclude
# daily, 04:30
crontab -l > /tmp/ct; echo '30 4 * * * /etc/ps4block.ipsync >/dev/null 2>&1' >> /tmp/ct; crontab /tmp/ct

That guard clause matters more than it looks. Without it, the first time the uplink is down when cron fires, you'd resolve nothing, write an empty list, and silently unblock everything.

On a 580 MHz MIPS core, 67 names × 3 resolvers takes about four minutes. Run it from cron, not interactively.


Step 6: making it survive

Everything lives in /etc, which is on the writable overlay. UCI changes need uci commit; plain files just need to be in /etc rather than /tmp.

For firmware upgrades, add your files to the backup list:

cat >> /etc/sysupgrade.conf <<'EOF'
/etc/ps4block.domains
/etc/ps4block.hosts
/etc/ps4block.ips
/etc/ps4block.fwinclude
/etc/ps4block.ipsync
EOF

Then actually reboot and re-test. I did it three times. The last one:

[ 7 s] router down
[59 s] router back

uplink       reassociated, DHCP lease reacquired
layer 1      dnsmasq: read /etc/ps4block.hosts - 134 addresses
layer 3      ps4block: PS4BLOCK loaded: 105 IPs
FORWARD      1 jump to PS4BLOCK present

One trap while verifying: fw3 puts custom rules in zone_lan_forward after a cold boot but in forwarding_lan_rule after a live restart. Both sit in the LAN→WAN path so behaviour is identical, but if you grep a single chain you'll conclude your rules vanished. Search the full ruleset by name:

iptables-save | grep -c 'Block-DoT-853'

The nuclear option

Everything above blocks updates while leaving PSN usable — online play, store, downloads.

If you'd rather the console never speak to Sony at all, dnsmasq's address= does wildcard domains, including every subdomain:

for d in playstation.com playstation.net playstation.org \
         scea.com sonyentertainmentnetwork.com ; do
  uci add_list dhcp.@dnsmasq[0].address="/$d/0.0.0.0"
  uci add_list dhcp.@dnsmasq[0].address="/$d/::"
done
uci commit dhcp; /etc/init.d/dnsmasq restart

Be clear about what this costs: no PSN sign-in, no online play, no store, no game downloads. It's a different product, not a stronger version of the same one. Reverse it with one command:

uci -q delete dhcp.@dnsmasq[0].address
uci commit dhcp && /etc/init.d/dnsmasq restart

(The same mechanism works for Nintendo — nintendo.*, nintendo-europe.com, nintendoswitch.*, nintendowifi.net — if you're doing this for a modded Switch instead.)


Testing your own setup

Don't trust it because you configured it. Run these from a machine on the LAN:

# 1. Does the block work at all?
nslookup dau01.ps4.update.playstation.net
# expect 0.0.0.0

# 2. Can I bypass it with an external resolver?
nslookup dau01.ps4.update.playstation.net 8.8.8.8
# expect 0.0.0.0 — proves the DNAT redirect is working

# 3. Can I bypass it with DoH?
Invoke-RestMethod -Uri "https://cloudflare-dns.com/dns-query?name=dau01.ps4.update.playstation.net&type=A" `
                  -Headers @{accept='application/dns-json'}
# expect a connection failure

# 4. Can I bypass it with DoT?
Test-NetConnection 1.1.1.1 -Port 853
# expect False

# 5. Can I reach a known update IP directly?
Test-NetConnection 23.50.131.198 -Port 443
# expect False

# 6. Did I break the normal internet?
Test-NetConnection github.com -Port 443
# expect True

One trap when testing from a laptop: if your machine has both Ethernet to the router and its own Wi-Fi to some other network, you have two default gateways and your test traffic may never touch the router at all. Force the source interface:

ping -S 192.168.2.25 8.8.8.8
tracert -4 -d 8.8.8.8        # hop 1 must be your router

I nearly declared victory on a test that was silently going out the laptop's own Wi-Fi.


What this doesn't cover

  • A stale DNS cache on the console. If the PS4 resolved an update host before you set this up, it still holds the address. Full power cycle clears it.
  • The console joined to a different network. The router can only police traffic that passes through it. If someone puts the PS4 on the house Wi-Fi directly, none of this applies. This is the realistic failure mode, and it's a human one.
  • DoH on an unlisted host. Covered above — a stock PS4 doesn't do this, but it's a real limit of the approach.
  • A VPN client. Any tunnel carries its own DNS. Not applicable to a PS4.

For a stock (or jailbroken) PS4, none of these are reachable in practice. The console resolves names only through the router, has no encrypted-DNS support, and doesn't ship hard-coded update-server addresses. The residual risk is operational, not architectural.


Signal quality is the thing that will actually break this

Worth saying because it's the least interesting problem and the one that bit me. My uplink degraded through the session:

-66 dBm  →  -77-85-88-78
tx retries: 9089    tx failed: 294

The link dropped and self-recovered once, and the first pings after a reboot showed 100% loss before settling. None of that is configuration — it's RF. If you're doing the Wi-Fi-client uplink trick, put the router near the access point, or run Ethernet.


References

The hardware

The RLTK420 I used is the previous revision of that model. Nothing in this post depends on the specific unit — any OpenWrt router with dnsmasq and fw3 will do — but if you want the same industrial-grade box with a SIM slot and a hardware watchdog, that's the current one.


Everything here was done on my own hardware, on my own console. Blocking your console from fetching firmware is a configuration choice about your own network; nothing in this post touches Sony's systems or anyone else's.

Related Articles

Same Category

Comments (0)

Newsletter

Stay updated! Get all the latest and greatest posts delivered straight to your inbox