checksec.sh 스크립트 NX 확인 관련 오동작

최근 pwnable.kr 문제 중 tiny_easy를 풀다 checksec 결과에서 NX 값이 잘못 표시되는 것을 확인하고 포스팅 합니다.

tester@ubuntu:~$ gdb tiny_easy
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from tiny_easy...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : disabled
gdb-peda$

위와 같이 checksec 명령어상에서는 NX가 켜져 있는것으로 확인되나 실제로는 꺼져 있습니다.

gdb-peda$ r
Starting program: /home/tester/tiny_easy

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x0
ECX: 0x0
EDX: 0x6d6f682f ('/hom')
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xbffff1f4 --> 0x804805a (add    BYTE PTR [eax],al)
EIP: 0x6d6f682f ('/hom')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x6d6f682f
[------------------------------------stack-------------------------------------]
0000| 0xbffff1f4 --> 0x804805a (add    BYTE PTR [eax],al)
0004| 0xbffff1f8 --> 0x0
0008| 0xbffff1fc --> 0xbffff3c9 ("XDG_VTNR=7")
0012| 0xbffff200 --> 0xbffff3d4 ("XDG_SESSION_ID=c2")
0016| 0xbffff204 --> 0xbffff3e6 ("XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/tester")
0020| 0xbffff208 --> 0xbffff418 ("SELINUX_INIT=YES")
0024| 0xbffff20c --> 0xbffff429 ("CLUTTER_IM_MODULE=xim")
0028| 0xbffff210 --> 0xbffff43f ("GPG_AGENT_INFO=/run/user/1000/keyring-vA2k21/gpg:0:1")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x6d6f682f in ?? ()
gdb-peda$ nxtest
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x0
ECX: 0x0
EDX: 0x6d6f682f ('/hom')
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xbffff1f4 --> 0xcccccccc
EIP: 0xbffff1f5 --> 0xcccccccc
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xbffff1ef:    add    BYTE PTR [ecx],al
   0xbffff1f1:    add    BYTE PTR [eax],al
   0xbffff1f3:    add    ah,cl
=> 0xbffff1f5:    int3  
   0xbffff1f6:    int3  
   0xbffff1f7:    int3  
   0xbffff1f8:    int3  
   0xbffff1f9:    int3
[------------------------------------stack-------------------------------------]
0000| 0xbffff1f4 --> 0xcccccccc
0004| 0xbffff1f8 --> 0xcccccccc
0008| 0xbffff1fc --> 0xbffff3c9 ("XDG_VTNR=7")
0012| 0xbffff200 --> 0xbffff3d4 ("XDG_SESSION_ID=c2")
0016| 0xbffff204 --> 0xbffff3e6 ("XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/tester")
0020| 0xbffff208 --> 0xbffff418 ("SELINUX_INIT=YES")
0024| 0xbffff20c --> 0xbffff429 ("CLUTTER_IM_MODULE=xim")
0028| 0xbffff210 --> 0xbffff43f ("GPG_AGENT_INFO=/run/user/1000/keyring-vA2k21/gpg:0:1")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGTRAP
0xbffff1f5 in ?? ()
NX test at stack: Executable
gdb-peda$

이에 checksec 명령어 관련 소스를 확인한 결과 peda에서는 아래 사이트의 스크립트를 참조함을 알 수 있었습니다.
http://www.trapkit.de/tools/checksec.html

또한 이와 동일한 오류가 pwntools github에 보고가 되어 있었습니다. (Thanks to laughfool)
https://github.com/Gallopsled/pwntools/issues/442

checksec.sh 코드를 리뷰한 결과 NX 확인은 아래 명령어의 결과값이 없을 경우 ENABLE로 판단함을 알 수 있었습니다.

tester@ubuntu:~$ readelf -W -l tiny_easy | grep "GNU_STACK" | grep "RWE"
tester@ubuntu:~$ readelf -W -l tiny_easy

Elf file type is EXEC (Executable file)
Entry point 0x8048054
There are 1 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x0005a 0x0005a R E 0x1000
tester@ubuntu:~$

tiny_easy 파일은 일반적인 ELF 파일과 다르게 GNU_STACK 이 없음을 알 수 있습니다.
이 경우 NX가 DISABLE로 되어야 하나 checksec.sh 스크립트에서는 이러한 예외사항을 고려하지 않았습니다.
이에 peda.py를 아래와 같이 수정 후 정상적인 결과값을 확인할 수 있었습니다.

def checksec(self, filename=None):
    """
    Check for various security options of binary (ref: http://www.trapkit.de/tools/checksec.sh)

    Args:
        - file: path name of file to check (String)

    Returns:
        - dictionary of (setting(String), status(Int)) (Dict)
    """
    result = {}
    result["RELRO"] = 0
    result["CANARY"] = 0
    result["NX"] = 1
    result["PIE"] = 0
    result["FORTIFY"] = 0

    if filename is None:
        filename = self.getfile()

    if not filename:
        return None

    out =  execute_external_command("%s -W -a \"%s\" 2>&1" % (config.READELF, filename))
    if "Error:" in out:
        return None

    for line in out.splitlines():
        if "GNU_RELRO" in line:
            result["RELRO"] |= 2
        if "BIND_NOW" in line:
            result["RELRO"] |= 1
        if "__stack_chk_fail" in line:
            result["CANARY"] = 1
+       # modified by JJoon
+       if "GNU_STACK" not in line:
+           result["NX"] = 0
        if "GNU_STACK" in line and "RWE" in line:
            result["NX"] = 0
        if "Type:" in line and "DYN (" in line:
            result["PIE"] = 4 # Dynamic Shared Object
        if "(DEBUG)" in line and result["PIE"] == 4:
            result["PIE"] = 1
        if "_chk@" in line:
            result["FORTIFY"] = 1

    if result["RELRO"] == 1:
        result["RELRO"] = 0 # ? | BIND_NOW + NO GNU_RELRO = NO PROTECTION
    # result["RELRO"] == 2 # Partial | NO BIND_NOW + GNU_RELRO
    # result["RELRO"] == 3 # Full | BIND_NOW + GNU_RELRO
    return result
tester@ubuntu:~$ gdb tiny_easy
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from tiny_easy...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : disabled
gdb-peda$

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다