OSED study materials
OSED study resources

Signed Length Check in a UDP Parser

OSED · Put your knowledge to work in context.

All case studies

Signed Length Check in a UDP Parser

While assessing a 32-bit Windows service, svcparse.exe, you reverse the packet handler at 0x00401200. The service reads a 16-bit length field at offset 0x10 of each UDP packet and copies the remainder into a 0x200-byte stack buffer using memcpy. The check is cmp ax, 200h; jg reject. The binary was linked with /GS, /NXCOMPAT, and /SAFESEH:NO. dumpbin /headers reports Dynamic base but also Relocation stripped; the OS has ASLR enabled. Sending a length of 0xFFFF with 65,535 bytes of A crashes the process with EIP=0x41414141, and the saved stack cookie is overwritten. You have no source code and cannot restart the service at will. The service runs as a low-privilege user on Windows 10 x86.

1. Explain why the length check is bypassed and what value is passed to `memcpy`. Include the relevant assembly-level reasoning.

Read the worked answer

The comparison is signed. cmp ax, 200h subtracts 0x200 from AX and sets flags; jg jumps only if the signed result is greater. The value 0xFFFF is -1 in 16-bit two's complement, so -1 > 512 is false and the branch is not taken. The handler then loads the same 16-bit field and passes it to memcpy as the size argument. Depending on whether the compiler emitted movzx eax, word ptr [packet+0x10] or movsx eax, word ptr [packet+0x10], the size is either 0x0000FFFF or 0xFFFFFFFF; both are far larger than the 0x200-byte destination. The copy therefore writes past the buffer, through the saved cookie, saved EBP, and return address. The crash at 0x41414141 confirms that the return address is controlled. The correct check would have been unsigned (ja) or a 32-bit comparison against 0x200.

2. Given the mitigations, outline a reliable exploitation plan. Address /GS, DEP, and ASLR explicitly, and state what primitive you need first, if any.

Read the worked answer

First, ASLR: the PE has no relocation section, so the main exe cannot be relocated and loads at its preferred base (e.g., 0x00400000). Only DLLs are randomized. That gives fixed gadget addresses in the exe. Second, /GS: the overflow overwrites the cookie, so a normal return would call __security_check_cookie and terminate. However, the oversized memcpy will fault when it reaches an unmapped guard page, raising an access violation before the function epilogue. That exception walks the SEH chain, which you have already overwritten. Because /SAFESEH:NO is set, the handler is not validated. Third, DEP: do not jump to the stack. Overwrite the SEH handler with a pop pop ret gadget in the fixed exe, set the next-SEH pointer to a short jump over the handler pointer into a ROP chain. The ROP chain must call VirtualProtect on the stack buffer and then jump to shellcode. You need the buffer's absolute address: at the first ROP gadget, ESP points into your buffer, so use a mov eax, esp; ret gadget, add the known offset to the shellcode, and pass that to VirtualProtect. No prior leak is required because the exe base is fixed and ESP gives you the stack location.

3. What would change if `/SAFESEH` were enabled? Would your plan still work? If not, what alternative would you consider?

Read the worked answer

/SAFESEH builds a table of valid exception handlers and checks the handler pointer during exception dispatch. A pop pop ret gadget in the exe would almost certainly not be in that table, so the exception would be rejected and the process terminated. The plan would fail. Alternatives: (a) find a separate information leak (e.g., an uninitialized stack read or a format string) to recover the stack cookie, then preserve it in the overflow and return normally into a ROP chain; (b) overwrite a local function pointer or C++ vtable pointer that is used after the copy but before the cookie check, giving a pre-epilogue control transfer; (c) use a heap overflow or another vulnerability that does not involve the stack cookie. Without a leak or a pre-epilogue pointer, a /GS + /SAFESEH binary is significantly harder to exploit reliably.