RELRO Checking is incorrect for crafted ELF file
ZhangZhuoSJTU opened this issue · 4 comments
Issue
RELRO checking in checksec
is incorrect for some crafted ELF file.
Debug Report
checksec may mis-identify an ELF without RELRO
as FULL RELRO
.
I have attached a crafted ELF file to trigger this bug, who has a crafted dynamic section entry with following value.
d_tag = DT_FLAGS,
d_un.d_val = DF_BIND_NOW,
It seems that checksec
only checks some features of RELRO binary compiled by standard compilers, instead of checking whether the GOT table will be actually set as NO-WRITE.
Output of checksec
:
$ ./checksec --file=easiest_patch
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 1 easiest_patch
Directly check the run-time permission:
gdb-peda$ p puts
$1 = {<text variable, no debug info>} 0x400480 <puts@plt>
gdb-peda$ nearpc 0x400480
0x400471: xor eax,0x200b92
0x400476: jmp QWORD PTR [rip+0x200b94] # 0x601010
0x40047c: nop DWORD PTR [rax+0x0]
0x400480 <puts@plt>: jmp QWORD PTR [rip+0x200b92] # 0x601018
0x400486 <puts@plt+6>: push 0x0
0x40048b <puts@plt+11>: jmp 0x400470
0x400490 <quick_exit@plt>: jmp QWORD PTR [rip+0x200b8a] # 0x601020
0x400496 <quick_exit@plt+6>: push 0x1
gdb-peda$ telescope 0x601018
0000| 0x601018 --> 0x7ffff7a64a30 (push r13)
0008| 0x601020 --> 0x7ffff7a27810 (lea rsi,[rip+0x3a7f09] # 0x7ffff7dcf720)
0016| 0x601028 --> 0x7ffff7af4180 (lea rax,[rip+0x2e0771] # 0x7ffff7dd48f8)
0024| 0x601030 --> 0x0
0032| 0x601038 --> 0x0
0040| 0x601040 --> 0x0
0048| 0x601048 --> 0x0
0056| 0x601050 --> 0x0
gdb-peda$ vmmap 0x601018
Start End Perm Name
0x00601000 0x00602000 rw-p /u/antor/u28/zhan3299/trojai/ZeroPatch/workshop/easiest_patch
It is easy to check that the GOT of puts
function is writable, but checksec reports is as FULL RELRO
sha256 of attached ELF: 7e5a5806d899fb00226487237b2a3a066cad8653be31d5210747e0beaf2cc1cc
It seems that checksec only checks some features of RELRO binary compiled by standard compilers, instead of checking whether the GOT table will be actually set as NO-WRITE.
As a side note, the overall issue with checksec.sh project in particular is that it just greps through various readelf
results and so it is very easy to fool it. As an example, if you compile this code without the stack protector, checksec.sh will still say the binary has stack canary:
void lol__stack_chk_fail() {}
int main() {
lol__stack_chk_fail();
}
PS: Since you tested some other tools, the checksec.rs also does misreport this case.
As a side note, the overall issue with checksec.sh project in particular is that it just greps through various readelf results and so it is very easy to fool it.
Is it in scope of checksec to take in account some troll code? I think people use it to inspect legitimate binaries. Checking spoofed binaries for hardening doesn't have much purpose as nobody will use them anyway. Also I don't see why someone would put troll code into legitimate binary only to confuse checksec. If they have bad intentions and put something malicious in code you run then it doesn't matter if it has rerlro or not.
Is it in scope of checksec to take in account some troll code?
Rather not, but checksec is often used for random binaries and to make statements of platform security.
Also I don't see why someone would put troll code into legitimate binary only to confuse checksec. If they have bad intentions and put something malicious in code you run then it doesn't matter if it has rerlro or not.
In theory, one could slip in a backdoor this way 🤷.
Both of these should be solved in the main branch now.
For the crafted elf, the virtual address does not resolve to an actual address in the binary so I added a search to get the address and then validate that it's a real address.
For the stack_chk_fail, we can do similar and look for the lack of an address, from looking at several binaries, it will always be all 0s and UND for the ndx where if you declare a function, it will have an address that won't be all 0's