Setup
- Install Node, Python 2.7, and Ruby
- Open ports UDP 53 and TCP 80 and 8081 on your firewall
- Run
npm install
,pip2 install flask
,gem install rubydns
- Start the DNS server with
sudo ruby rdns.rb
(drop sudo for Windows) - Start the web server with
sudo python serve.py
- Start watchify with
npm start
- Start the shell with
node debug.js
- Point your Switch to the DNS server
- Go to the eShop or another area that will trigger the captive portal
- Watch the shell connect
Shell
The default way to work with PegaSwitch is via the shell. Type help
after the Switch connects to get a list of commands.
To disable the shell (and just work with the API), comment out the following line in exploit/main.js
:
setupListener(sc);
API
Conventions
64-bit values (pointers, primarily) are represented using a JavaScript array containing [lo, hi]
, where each is 32-bit.
Utility Functions
paddr(address)
-- Convert a 64-bit value into a hex string representationadd2(a, b)
-- Adds two 64-bit values or adds a 64-bit value and a numbernullptr(address)
-- Returns true if the given 64-bit value is 0eq(a, b)
-- Returns true if the two 64-bit values are equalparseAddr(address)
-- Takes a hex string and parses into a 64-bit value
SploitCore
Sploitcore is the centerpoint of PegaSwitch, providing all of the core functionality and most of the important API. These are all methods on the sploitcore object.
dumpaddr(address, count)
-- Takes an address and a number of 32-bit values to logread4(address, offset)
-- Reads a 32-bit value fromaddress + offset * 4
read8(address, offset)
-- Reads a 64-bit value fromaddress + offset * 4
write4(value, address, offset)
-- Writes a 32-bit value toaddress + offset * 4
write8(value, address, offset)
-- Writes a 64-bit value toaddress + offset * 4
memview(address, size, cb)
-- Callscb
with an ArrayBuffer pointing to the view of memory requested. DO NOT keep that view or any object using it around; you will tank the GC and your Switch will crashgetAddr(obj)
-- Returns the address of a given JavaScript objectmref(offset)
-- Returns the address of the main module (the application binary itself) plus the given (32-bit) offsetgetBase()
-- Returns the base address of WebKitgetSP()
-- Returns the current stack pointer (current as of a function call in JS), primarily useful for JOP/ROP chainsmalloc(bytes)
-- Returns an address to an allocated bufferfree(addr)
-- Frees a bufferbridge
andcall
-- Documented belowsvc(id, registers, dump_regs)
-- Call a specific SVC, passing an array of registers and optionally dumping all regs (dump_regs == true/false)getTLS()
-- Gets address of TLSstr2buf(str)
-- Allocates a buffer for a null-terminated string and returns the addressreadString(addr, length)
-- Reads a string fromaddr
. If length is not passed or -1, the string is expected to be null-terminatedgc()
-- Force garbage collection
Call
sploitcore.call
allows you to call native functions by address. It takes the following parameters, with the first being required:
address
- Function address. Either a 32-bit offset from the main module address, or a 64-bit absolute pointerargs
- Array of arguments, to go in x0+fargs
- Array of floats, to go in d0+registers
- Array of raw registers (x16 and x30 not assignable)dump_regs
- Boolean to set whether registers should be dumped upon return
This function always returns the 64-bit value in x0.
Bridge
Bridge allows you to wrap a native function into a JavaScript function. Example:
var strlen = sc.bridge(0x43A6E8, int, char_p);
log(strlen('foo')); // Logs 3 to the console
The first parameter is the address (same format as call
), second is the return type, the rest are arguments.
The following are valid types:
null
-- Used forvoid
returnsint
void_p
-- Arbitrary pointerchar_p
-- String pointerfloat
-- Floating point argument; currently only supported for arguments, not returns