libc leak없이 libc 메모리 주소에 aaw를 해야하는 상황에서 많은 삽질을 하다가 해당 기법을 알게되었는데요..

Stack Pivot은 특정한 쓰기 가능한 공간에 Fake Stack을 구성해 놓고 rsp로

스택의 위치를 바꿔 Chanining을 이어갈 수 있는 기법입니다.

해당 기법은 SFP와 leave Gadget을 사용하므로 최소한 RET 주소까지는 오버플로우가 일어나야 합니다.

보통 오버플로우 기법을 이용할 때 입력 공간이 부족한 경우 이용하게 됩니다.

 

조건

  • 최소한 RET까지 오버플로우가 일어나야 한다
  • leave ; ret ; 가젯이 있어야 함
    leave 의 경우 mov rsp , rbp ; pop rbp ; 와 같이 Gadget 조각을 모아 조건을 성립시켜줘도 됨
  • Fake Stack의 위치는 Stack처럼 사용하므로 RW 권한이 있어야 함

 

SFP에 Fake Stack의 주소를 넣고

RET에 leave Gaget을 넣게 되면 rsp를 원하는 위치로 지정할 수 있습니다.

 

 

이후 Fake Stack의 스택프레임에 맞춰 Chain을 이어나갈 수 있게 됩니다.

기본적인 개념은 이렇습니다. 하지만 이를 응용하면 생각지도 못한 위치로 aaw가 가능하게 됩니다.

 

AAW 응용


libc leak을 하지 않은 상황에서 라이브러리 주소에 특정 바이트를 적는 것은 어렵습니다. 그런데 

Stack pivot을 사용하여 라이브러리 주소를 담고있는 특정 주소의 -0x8의 위치에 pop rsi 가젯을 위치시키면

주소값이 read함수의 인자로 넘어가면서 자연스레 해당 라이브러리 주소에 원하는 byte를 적을 수 있게 됩니다.

Fake Stack의 구성은 다음과 같습니다.

영역의 크기를 잘 고려해서 페이로드를 짜야하지만, 이런 창의적인 방법으로 libc leak 없이 aaw가 가능합니다!

 

정교한 익스를 위한 추가사항

Fake Stack의 Chain별 길이를 조절하는게 중요합니다. 예를 들어, FULL RELRO가 걸려있는 ELF에서

특정 영역에 Fake Stack을 생성했다면 길이가 너무 길어 .got영역와 같은 영역을 페이로드가 침범할 수 있습니다.

이런 경우 바이너리가 바로 죽어버리게 되는데 이는 익스플로잇에 민감한 사항이 될 수 있습니다.

이럴 경우, RW가 가능한 메모리를 우선 확인하고 사이즈를 조절한 뒤 Chain을 연결하여 ROP를 터트리는 방향을

고려해봐야 합니다.

'System' 카테고리의 다른 글

Linux] Seccomp  (0) 2022.04.28
DLL Load(IAT, EAT)  (0) 2022.03.27

Seccomp


리눅스 커널에서 제공하는 프로세스 샌드박싱 기법입니다.

규칙에 맞게 syscall 호출을 허용 및 차단하여 쉘 코드의 공격을 방지하는 리눅스 보안

매커니즘이라고 볼 수 있겠습니다.

Seccomp 기능은 prctl( ) 함수를 통해 사용할 수 있습니다.

prctl(PR_SET_SECCOMP, mode, &sock_fprog)

struct sock_fprog {
	unsigned short len; // BPF 인스트럭션 개수
    struct sock_filter *filter; BPF // BPF 인스트럭션 배열
};

struct sock_filter {            /* 필터 블록 */
    __u16 code;                 /* 실제 필터 코드 */
    __u8  jt;                   /* 참 점프 */
    __u8  jf;                   /* 거짓 점프 */
    __u32 k;                    /* 범용 다용도 필드 */
};

첫 번째 인자는 seccomp의 설정

두 번째 인자는 seccomp의 mode

 seccomp.h

#define SECCOMP_MODE_DISABLED	0 
#define SECCOMP_MODE_STRICT	1 
#define SECCOMP_MODE_FILTER	2

/*
https://code.woboq.org/linux/include/linux/seccomp.h.html

 

sock_fprog 구조체의 경우 seccomp mode가 FILTER이고 BPF를 사용하여 필터를 추가할 때
사용하게 됩니다.

seccomp의 모드를 먼저 살펴보겠습니다.

 

SECCOMP_SET_MODE_STRICT

허용되는 시스템 호출이 read, write, _exit, sigreturn 입니다. 그 외의 다른 시스템 호출은

SIGKILL 시그널을 전달하여 프로그램을 종료하게 됩니다.

 

SECCOMP_SET_MODE_FILTER

블랙리스트 혹은 화이트리스트 방식으로 필터를 만들어 적용합니다.

필터를 만드는 2가지의 방법은 다음과 같습니다.

라이브러리 함수를 사용하는 경우

#include<seccomp.h>

typedef void & scmp_filter_ctx;

scmp_filter_ctx seccomp_init(uint32_t def_action);
/* def_action
	SCMP_ACT_KILL : 필터를 제외한 나머지는 모두 차단
    	SCMP_ACT_ALLOW: 필터를 제외한 나머지는 모두 허용
*/

seccomp_rule_add(scmp_filter_ctx, uniot32_t action, int syscall, ...)
/* 필터를 추가 */

seccomp_load(scmp_filter_ctx)
/* seccomp 필터를 커널에 로드. */
/*
참고 : https://manpages.debian.org/testing/libseccomp-dev/

 

버클리 패킷 필터(BPF)를 사용

BPF_STMT(opcode, operand)
/* operand에 해당하는 값을 opcode로 가져옴 */

BPF_JUMP(opcode, operand, true_offset, false_offset)
/* operand와 opcode를 비교 후 결과에 따라 특정 offset으로 분기 */

#define ALLOW_SYSCALL(name) \
	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_##name, 0, 1),\
	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
    
#define KILL_PROCESS \
	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
    
/*
참고 : https://outflux.net/teach-seccomp/step-2/seccomp-bpf.h

sock_filter 구조체에 ALLOW_SYSCALL 매크로를 사용하여 필터를 만들고 sock_fprog 구조체를 사용해

prctl( ) 함수의 3번째 인자로 전달합니다.

 

Bypass Seccomp


1) Seccomp를 우회할 수 있는 다른 시스템 콜을 사용합니다. 주의해야 할 점은 다른 시스템 콜에

크게 의존하지 않는 시스템 콜을 호출해야 한다는 점입니다.

2 ) FILTER Mode에서 라이브러리 함수를 사용한 경우x86-64와 x32에서 명령어의 호완을 지원하기 위해

Seccomp 사용 시 아키텍처의 구조에 따라 do_syscall_64 , do_syscall_32 호출의 분기점을 생성합니다.

x86_64 에서 필터가 적용되어 있다면 do_syscall_32 로 우회하여 시스템 콜 호출이 가능하게 됩니다.

 

추가로, CTF에서 SECCOMP가 적용된 바이너리를 분석할 때에 강력한 도구인 seccomp-tools 를 사용하여 

바이너리 분석을 수월히 할 수 있습니다.

 

 

 

'System' 카테고리의 다른 글

Stack Pivot  (0) 2022.05.17
DLL Load(IAT, EAT)  (0) 2022.03.27

기존에는 각 프로그램 별로 라이브러리를 포함시켜 사용을 했습니다.
컴파일러가 C 라이브러리에서 Binary 코드를 그대로 가져왔다고 볼 수 있습니다.
그런데 Window가 멀티태스킹을 지원하게 되면서
여러 프로그램이 동시에 실행이 가능해졌고 DLL의 이런 로딩 방식은 메모리의 낭비로 이어졌습니다.

따라서 프로그램에 라이브러리를 포함시키지 않고 메모리에 한 번 로딩된 라이브러리의 리소스를
Memory Mapping 기능을 사용해 여러 프로세스에서 공유해 쓰는 방식으로 라이브러리를 사용하게 되었습니다.

라이브러리는 링킹의 시점에 따라 Static과 Dynamic의 방식으로 나눌 수 있습니다.

  • Static Link : 컴파일 시점에서 라이브러리가 링킹 단계에서 연결되는 방식
    *.lib 파일을 링킹 과정에서 포함하게 되는데 해당 파일은 라이브러리 전체 코드를 포함하는 binary
  • Dynamic Link : 실행 파일에서 해당 라이브러리 기능을 사용 시 라이브러리 파일을 참고하여 기능을 호출
    마찬가지로 .lib파일을 이용하지만 여기에는 DLL이 제공하고자 하는 함수 정보를 가지는 파일

 

 

IAT


프로그램이 어떤 라이브러리에서 어떤 함수를 사용하는지에 대한 테이블
라이브러리가 PE의 NT 헤더에 명시된 ImageBase에 로딩된다고 보장할 수 없기 때문에 IAT의 RVA 값을 통해
함수에 접근합니다. 프로그램 안에 사용하고자 하는 함수에 대한 모든 정보가 없어도 주소만 가지고
DLL로 넘어가 함수 정보를 가져다쓰므로 용량이 크지도 않고, 메모리도 크게 점유하지 않는다는 점이 있습니다.

 

PE 헤더의 NT_Optional_Header -> DataDirectory [1]의 위치에 정보가 명시되어 있습니다.
DataDirectory[1]의 구조체의 경우 다음과 같습니다.

 

IAT는 IMAGE_IMPORT_DESCRIPTOR 구조체의 형태로 존재하는데요.

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
   union {
	DWORD Characteristics;
	DWORD OriginalFirstThunk;
    };
    DWORD TimeDateStamp;
    DWORD ForwarderChain;
    DWORD Name;
    DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;

멤버의 의미를 살펴보면

  • OriginalFirstThunk : Import Name Table의 주소
  • Name : 라이브러리 이름의 주소
  • FirstThunk : IAT( Import Address Table )의 주소

winDBG로 notepad.exe를 분석해 해당 구조체를 살펴보겠습니다.

 

Import Directory ( RVA : 0x2D0A8 )

멤버별로 비교해보면 

  • OriginalFirstThunk : 0x2d3c0
  • Name : 0x2e1d8
  • FirstThunk : 0x268b8

임을 확인할 수 있습니다.

 

Import되는 함수를 찾을 수 있음.

 

 

 

EAT


다른 파일에게 자신이 제공하는 함수들에 대한 테이블

PE 헤더의 NT_Optional_Header -> DataDirectory[0] 의 위치에 정보가 명시되어 있습니다.
DataDirectory[0]의 구조체의 경우 다음과 같습니다.

EAT는 IMAGE_EXPORT_DIRECTORY 구조체의 형태로 존재합니다.

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD Characteristics;
    DWORD TimeDateStamp;
    WORD MajorVersion;
    WORD MinorVersion;
    DWORD Name;
    DWORD Base;
    DWORD NumberOfFunctions;
    DWORD NumberOfNames;
    DWORD AddressOfFunctions;
    DWORD AddressOfNames;
    DWORD AddressOfNameOrdinals;
 } IMAGE_EXPORT_DEIRECTORY, *PIMAGE_EXPORT_DIRECTORY

중요 멤버로는

  • AddressOfFunctions : Export 함수 주소 배열
  • AddressOfNameOrdinals : Ordinal 주소 배열

EAT의 경우 제공하는 함수를 함수의 이름 RVA에서 몇 번째 인덱스에 있는지를 확인합니다.
찾은 인덱스를 바탕으로 해당 인덱스에 들어 있는 Ordinal 값을 참조해
원하는 함수의 시작 주소를 얻는 과정을 거치게 됩니다.

Export Directory ( RVA : 0x9A1E0 )

멤버별로 비교해보면

  • AddressOfFunctions : 0x9a208
  • AddressOfNames : 0x9bb8c
  • AddressOfNameOrdinals : 0x9d510

임을 확인할 수 있습니다.

Export되는 함수를 찾을 수 있음.

 

 

'System' 카테고리의 다른 글

Stack Pivot  (0) 2022.05.17
Linux] Seccomp  (0) 2022.04.28

+ Recent posts