Since HENkaku has been released at the end of July, 2016, Sony has rolled out two system updates: 3.61 and 3.63. These both were aimed at fixing HENkaku and in this post I’m going to explain how they did it.

Firmware version 3.61 (released August 8, 2016) only fixed the WebKit bug and the stack infoleak. Firmware 3.63 fixed the SceNetPs ioctl UaF, which is the core of the exploit, and PSN spoofing. Interestingly, 3.63 was released on November 1, 2016, two months after my post about how the exploit worked went public.

The stack leak fix is not interesting and WebKit fix is already documented here. Let’s cut to the fun stuff.

Fixing UaF 101

The root case of the bug is lack of reference counting. As such I expected to find it in the 3.63 sceNetIoctl function code. What was really surprising is that they went through the whole module and added socket reference counting everywhere.

The implementation looks like this:

socket = get_socket_by_id(s_, &v22, 0);
// ...
++*(_DWORD *)(socket + 0xFC);                 // incref
// perform operation etc
bnet_sorele(socket);

What’s bnet_sorele? This is a helper function which decreases refcnt and frees the socket when it reaches zero.

But wait, there’s more

This fix is already enough to kill the HENkaku bug, and actually is a bit of an overkill. However, looking further into the SceNetPs module, we found that they have added a new exploit mitigation. Before doing any “dangerous” operations with a socket, for example:

    v15 = (*(int (__fastcall **)(int, signed int, unsigned int, char *))(*(_DWORD *)(socket_ + 24) + 28))(
            socket_,
            11,
            v5,
            v11);

they now perform a sanity check:

    bnet_socket_sanity_check(socket_);
    LOWORD(v13) = -32276;
    HIWORD(v13) = (unsigned int)&dword_1EA81EC;
    v14 = *v13;
    v15 = (*(int (__fastcall **)(int, signed int, unsigned int, char *))(*(_DWORD *)(socket_ + 24) + 28))(
            socket_,
            11,
            v5,
            v11);

What’s the sanity check? You can see the pseudocode here. It checks some socket fields to be fixed function pointers, check that vtable pointer points to SceNetPs .data section, checks that vtable function pointers point into SceNetPs .text section. In the end, it’s basically a poor man’s CFI.

Conclusion

It’s very surprising how throughout the fix is. Considering that PS Vita is a failure, it does not make any sense to spend so much effort fixing this bug. Unless… Sony is working on a new Vita to compete with Nintendo Switch? ヽ(°〇°)ノ Let me know what you think about it.