# DDexec / EverythingExec {{#include ../../../banners/hacktricks-training.md}} ## Context 리눅스에서 프로그램을 실행하려면 파일로 존재해야 하며, 파일 시스템 계층을 통해 어떤 방식으로든 접근 가능해야 합니다(이는 `execve()`가 작동하는 방식입니다). 이 파일은 디스크에 있거나 램(tmpfs, memfd)에 있을 수 있지만, 파일 경로가 필요합니다. 이로 인해 리눅스 시스템에서 실행되는 것을 쉽게 제어할 수 있으며, 위협 및 공격자의 도구를 감지하거나 그들이 아무것도 실행하지 못하도록 방지하는 것이 용이합니다(_예: 비특권 사용자가 실행 파일을 아무 곳에나 배치하는 것을 허용하지 않음). 하지만 이 기술은 모든 것을 바꾸기 위해 존재합니다. 원하는 프로세스를 시작할 수 없다면... **이미 존재하는 프로세스를 탈취합니다**. 이 기술은 **읽기 전용, noexec, 파일 이름 화이트리스트, 해시 화이트리스트와 같은 일반적인 보호 기술을 우회할 수 있게 해줍니다**. ## Dependencies 최종 스크립트는 작동하기 위해 다음 도구에 의존하며, 공격하는 시스템에서 접근 가능해야 합니다(기본적으로 모든 곳에서 이들을 찾을 수 있습니다): ``` dd bash | zsh | ash (busybox) head tail cut grep od readlink wc tr base64 ``` ## 기술 프로세스의 메모리를 임의로 수정할 수 있다면, 해당 프로세스를 장악할 수 있습니다. 이는 이미 존재하는 프로세스를 가로채고 다른 프로그램으로 교체하는 데 사용될 수 있습니다. 우리는 `ptrace()` 시스템 호출을 사용하거나 (이는 시스템에서 시스템 호출을 실행할 수 있는 능력이나 gdb가 필요합니다) 더 흥미롭게도 `/proc/$pid/mem`에 쓰는 방법으로 이를 달성할 수 있습니다. 파일 `/proc/$pid/mem`은 프로세스의 전체 주소 공간의 1:1 매핑입니다 (_예: x86-64에서 `0x0000000000000000`에서 `0x7ffffffffffff000`까지). 이는 오프셋 `x`에서 이 파일을 읽거나 쓰는 것이 가상 주소 `x`에서 내용을 읽거나 수정하는 것과 동일하다는 것을 의미합니다. 이제 우리는 네 가지 기본 문제에 직면하게 됩니다: - 일반적으로 루트와 파일의 프로그램 소유자만 수정할 수 있습니다. - ASLR. - 프로그램의 주소 공간에 매핑되지 않은 주소를 읽거나 쓰려고 하면 I/O 오류가 발생합니다. 이 문제들은 완벽하지는 않지만 좋은 해결책이 있습니다: - 대부분의 셸 인터프리터는 자식 프로세스가 상속받을 파일 설명자를 생성할 수 있도록 허용합니다. 우리는 쓰기 권한이 있는 셸의 `mem` 파일을 가리키는 fd를 생성할 수 있습니다... 따라서 해당 fd를 사용하는 자식 프로세스는 셸의 메모리를 수정할 수 있습니다. - ASLR은 문제도 아닙니다. 우리는 셸의 `maps` 파일이나 procfs의 다른 파일을 확인하여 프로세스의 주소 공간에 대한 정보를 얻을 수 있습니다. - 따라서 우리는 파일에서 `lseek()`를 수행해야 합니다. 셸에서는 악명 높은 `dd`를 사용하지 않고는 이를 수행할 수 없습니다. ### 더 자세히 단계는 상대적으로 쉽고 이해하는 데 어떤 전문 지식도 필요하지 않습니다: - 실행할 바이너리와 로더를 파싱하여 필요한 매핑을 찾습니다. 그런 다음, 대략적으로 커널이 `execve()`를 호출할 때 수행하는 것과 동일한 단계를 수행하는 "셸" 코드를 작성합니다: - 해당 매핑을 생성합니다. - 바이너리를 그 안으로 읽어들입니다. - 권한을 설정합니다. - 마지막으로 프로그램의 인수로 스택을 초기화하고 로더에 필요한 보조 벡터를 배치합니다. - 로더로 점프하여 나머지를 수행하게 합니다 (프로그램에 필요한 라이브러리를 로드합니다). - 실행 중인 시스템 호출 후 프로세스가 반환할 주소를 `syscall` 파일에서 가져옵니다. - 해당 위치를 덮어씌우고, 이는 실행 가능하며, 우리의 셸코드로 대체합니다 (우리는 `mem`을 통해 쓰기 불가능한 페이지를 수정할 수 있습니다). - 실행할 프로그램을 프로세스의 stdin으로 전달합니다 (해당 "셸" 코드에 의해 `read()`됩니다). - 이 시점에서 로더는 프로그램에 필요한 라이브러리를 로드하고 그 안으로 점프하는 역할을 합니다. **도구를 확인하세요** [**https://github.com/arget13/DDexec**](https://github.com/arget13/DDexec) ## EverythingExec `dd`에 대한 여러 대안이 있으며, 그 중 하나인 `tail`은 현재 `mem` 파일을 통해 `lseek()`하는 데 사용되는 기본 프로그램입니다 (이는 `dd`를 사용하는 유일한 목적이었습니다). 이러한 대안은: ```bash tail hexdump cmp xxd ``` 변수 `SEEKER`를 설정하면 사용되는 seeker를 변경할 수 있습니다, _예:_: ```bash SEEKER=cmp bash ddexec.sh ls -l <<< $(base64 -w0 /bin/ls) ``` 스크립트에 구현되지 않은 다른 유효한 seeker를 찾으면 `SEEKER_ARGS` 변수를 설정하여 여전히 사용할 수 있습니다: ```bash SEEKER=xxd SEEKER_ARGS='-s $offset' zsh ddexec.sh ls -l <<< $(base64 -w0 /bin/ls) ``` 이것을 차단하세요, EDRs. ## 참고문헌 - [https://github.com/arget13/DDexec](https://github.com/arget13/DDexec) {{#include ../../../banners/hacktricks-training.md}}