어셈블리 파일이 프로젝트에 포함 된 경우 mmap에서 예기치 않은 exec 권한


94

나는 이것으로 벽에 머리를 두드리고 있습니다.

내 프로젝트 mmap에서 매핑 ( /proc/self/maps)으로 메모리를 할당 할 때 읽기 가능한 메모리 만 요청 했지만 읽기 가능하고 실행 가능한 영역임을 보여줍니다 .

strace (좋아 보이는) 및 기타 디버깅을 살펴본 후에 프로젝트에서 어셈블리 파일을 제거하고 순수한 C 만 남겨 두는이 이상한 문제를 피하는 것으로 보이는 유일한 것을 확인할 수있었습니다 (무엇?!).

그래서 여기 이상한 예가 있습니다. 우분투 19.04와 기본 gcc에서 작업하고 있습니다.

ASM 파일 (비어 있음)을 사용하여 대상 실행 파일을 컴파일하면 mmap읽을 수 있고 실행 가능한 영역이 반환됩니다. /proc/self/maps내 예제에 포함 된 출력을 참조하십시오 .

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

example.s : 빈 파일입니다!

출력

ASM 포함 버전

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

ASM 포함 버전이없는 경우

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

5
이것은 정말 이상합니다.
fuz

6
나는 GCC (CMake 없음)로 이것을 재현 할 수 있었으므로 예제를보다 작게 만들기 위해 질문을 편집했습니다.
Joseph Sible-Reinstate Monica


당신이 바로 될 수는 그 해답의 일부는 READ_IMPLIES_EXEC 인물 주위에있다
벤 Hirschberg

로 소스 파일을 조립하십시오 -Wa,--noexecstack.
jww

답변:


90

리눅스는이 실행 도메인 이라고 READ_IMPLIES_EXEC할당 모든 페이지 원인 PROT_READ도 주어질를 PROT_EXEC. 이 프로그램은 자체 활성화 여부를 보여줍니다.

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

.s파일 과 함께 컴파일하면 파일이 활성화되어 있지만 파일이 없으면 비활성화됩니다. 이 값의 초기 값은 바이너리의 ELF 메타 정보에서 비롯됩니다 . 마십시오 readelf -Wl example. 빈 .s파일 없이 컴파일하면 다음 줄이 표시됩니다 .

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

그러나 이것으로 컴파일 할 때 :

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

RWE그냥 대신 참고하십시오 RW. 그 이유는 링커에서 명시 적으로 지시하지 않는 한 어셈블리 파일에 read-implies-exec가 필요하다고 가정하고 프로그램의 일부에 read-implies-exec가 필요한 경우 전체 프로그램에 대해 활성화되어 있기 때문입니다. . GCC가 컴파일하는 어셈블리 파일은이 줄과 함께 이것이 필요하지 않다고 알려줍니다 (으로 컴파일하면 이것을 볼 수 있습니다 -S).

        .section        .note.GNU-stack,"",@progbits

그 줄을에 넣으면 example.s링커에게 필요하지 않다고 알려 주면 프로그램이 예상대로 작동합니다.


13
이런 쓰레기, 그것은 이상한 기본값입니다. 툴체인은 noexec 이전에 존재했으며 noexec를 기본값으로 설정하면 문제가 발생했을 수 있습니다. 이제 NASM / YASM과 같은 다른 어셈블러가 .o파일을 만드는 방법이 궁금 합니다! 그러나 어쨌든 gcc -zexecstack이것이 이것이 사용 하는 메커니즘이며 스택뿐만 아니라 모든 것을 실행 가능한 이유 라고 생각합니다 .
Peter Cordes

23
@Peter-어셈블러를 사용하는 Botan, Crypto ++ 및 OpenSSL과 같은 프로젝트가 추가되는 이유 -Wa,--noexecstack입니다. 나는 그것이 매우 불쾌한 날카로운 가장자리라고 생각합니다. nx 스택의 자동 손실은 보안 취약점이되어야합니다. Binutil 사람들이 고쳐야합니다.
jww

14
@jww이 보안 문제는 아무도 전에보고되지 것을, 참으로 이상하다
벤 Hirschberg

4
+1, 그러나이 답변은 라인의 의미 / 논리 .note.GNU-stack,"",@progbits가 설명 된 경우 훨씬 더 나을 것입니다. 지금은 "이 마법의 문자열이이 효과를 일으킨다"와 같은 불투명하지만 문자열은 분명히 일종의 의미론.
mtraceur

33

GNU 특정 섹션 지시문 변형을 사용하여 어셈블리 파일을 수정하는 대신 어셈블리 파일 -Wa,--noexecstack을 빌드하기 위해 명령 줄에 추가 할 수 있습니다 . 예를 들어, musl에서 어떻게하는지보십시오 configure:

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

통합 어셈블러가있는 적어도 일부 버전의 clang은 --noexecstack(없이 -Wa) 전달해야 할 수도 있으므로 configure 스크립트는 둘 다 확인하고 어느 것이 허용되는지 확인해야합니다.

당신은 또한 사용할 수 있습니다 -Wl,-z,noexecstack(의 링크시 LDFLAGS같은 결과를 얻을 수). 이 방법의 단점은 .a다른 소프트웨어에서 사용하기 위해 프로젝트에서 정적 ( ) 라이브러리 파일을 생성하는 경우 도움이되지 않는다는 것 입니다. 다른 프로그램에서 사용할 때 링크 타임 옵션을 제어하지 않기 때문입니다.


1
흠 ... 나는 당신 이이 게시물을 읽기 전에 리치 펠커인지 몰랐다. 왜 표시 이름이 달라지지 않습니까?
SS Anne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.