Enviroment Variables & Attacks
Enviroment Variables
환경변수
운영체제가 실행중인 프로세스의 환경을 정의하는 동적 변수의 집합
예를들어 PATH 변수에서 쉘이 명확한 경로를 받지 못했을 때 여기서 찾는다.
환경변수에 접근하는 법

이렇게 메인문에서 접근도 가능하지만

전역변수로 선언하는 것이 더 권장된다.
프로세스가 환경변수 얻는 법
프로세스는 두가지 방법으로 얻을 수 있다
- fork()
위 명령어를 실행하면 새로운 자식프로세스가 생성된다.
이 때 부모의 환경변수를 상속받는다.
- execve()
이건 현재 실행중인 프로세스를 완전히 덮어쓰기 하는 것이다.
이 때 기존 환경변수..는 다 날아가고 새롭게 들어온다.

이 때 envp[]부분에 환경변수가 들어오게 된다.

/user/bin/env는 현재 환경변수를 출력하는 명령어이다.
이 때 새로운 환경변수를 의미하는 newenv배열을 만들었다.
만약 사용자가 1을 입력했으면 환경변수로 null을, 2를 입력했으면 newenv를, 3을 입력했으면 현재의 환경변수를 넘겨준다.

그 결과 다음과 같이 출력되는 것을 볼 수 있다.

여기서 envp와 environ의 차이는?
envp는 main 내부에서 존재하고, environ은 글로벌 변수이다.
처음에는 둘이 같은 곳을 가르킨다. 근데 만약 환경변수가 변화되면 environ은 환경변수가 새로 생성된 힙을 가르키게된다.
쉘 변수와 환경변수 차이
쉘변수는 쉘에서만 사용 가능하다.
쉘은 built-in-commend라는 쉘 내부 전용 명령어로 유저에게 쉘변수를 생성하고 삭제할 수 있게 한다.

/proc는 리눅스의 가상 파일시스템이다. 이건 각 프로세스에 디렉토리를 포함하고있고, 각 디렉토리는 id가 있다.
각 프로세스는 environ이라는 프로세스 내 환경변수를 포함하는 가상 파일을 가지고있다.
현재 프로세스를 출력하려면

이렇게 하면 된다. $$는 현재 프로세스의 id를 뜻한다
배쉬쉘에서 env가 실행되면(새로운 프로세스 실행) 그건 자식프로세스로 실행된다. 그러므로 그건 진짜 자신의 환경변수가 아니라 상속된 자식의 환경변수이다.
쉘 프로그램이 실행되면 부모프로세스로부터 환경변수를 자신의 쉘 변수로 복사한다.
그래서 쉘변수 변경은 환경변수에 영향을 미치지 않는다.
그래서 아예 처음은 쉘변수와 환경변수가 같지만, 쉘변수에 변동이 생기면 그때부터 달라진다.

이 예시를 보면 처음에 LONGNAME= seed는 쉘변수, 환경변수에 둘 다 있지만
환경변수에서 이를 bob으로 바꾸거나 삭제해도 쉘변수에는 적용이 되지만 환경변수에는 적용이 되지 않는 것을 볼 수 있다.

전체적인 구조는 다음과 같다.
쉘변수는 child프로세스의 환경변수에 영향을 미칠 수 있는데 이건 쉘변수가 exported로 승격되어야 가능하다.
승격되기 전 쉘변수들은 자식프로세스로 전달되지 않는다.

여기서 env는 현재 프로세스의 환경변수를 출력하는 명령어이다.
이 때 LONGNAME3은 export 되어 환경변수에 반영되었지만, LONGNAME2는 반영되지 못했다.
환경변수는 위험하다 user가 값을 세팅할 수 있기 때문이다.
Attacks via Dynamnic Linker
Linking는 프로그램 실행 시 필요한 함수를 외부 라이브러리에서 가져온다
linking은 runtime 혹은 compile 시점에 실행된다.
Static Linking
printf()와 같은 함수의 라이브러리코드를 결합한다.
이 코드는 그래서 용량이 dynamic program에 비해서 100배 크다.
Dynamic Linker
실행시점에 코드가 들어간다.
실행되기 전, 메모리에 실행가능한 코드들이 올라간다.


ldd는 어떤 라이브러리에 의존하는지 보여준다.
static 은 아무것도 안뜨지만, dynamic에서는 여러 라이브러리가 뜨는 것을 확인할 수 있다.
Dynamic Linker은 메모리가 절약되고, 이는 실행시점에 코드가 결정된다는 뜻이다.
하지만 이렇게 하면 파일 경로 조작해서 악성 라이브러리 코드를 주입할수도 있다.
Dynamic Linker을 사용한 공격
LD_PRELOAD는 링커에 의해 처음으로 찾게 되는 라이브러리이다.
만약 함수가 찾아지지 않았다면 LD_LIBRARY_PATH에서 찾는다.
이 두 값들은 누구나 설정 가능해서 이것들은 linking process를 컨트롤할 수 있는 기회를 제공한다.
이것이 만약 Set-UID(파일 소유자의 권한으로 실행)에서 된다면 이것은 보안침해가 될 수 있다.

공격자는 이렇게 i am not sleeping이라는 문구를 출력하는 악성 코드를 가지고 있는 라이브러리를 만든다.

그 다음 이 라이브러리를 컴파일하고, LD_PRELOAD에 업로드하면 이렇게 코드가 실행된 것을 볼 수 있다.
만약 이게 Set-UID에서도 가능하면 큰일이다.

그런데 이렇게 Set-UID에서는 실행되지 않는다.
그 이유는 보안상으로 EUID와 RUID가 다르면 LD_PRELOAD와 LD_LIBARY_PATH 환경변수를 무시하게 되어있기 때문이다.


여기서 myenv는 Set-UID 프로그램이다.
그래서 이 때 myenv에서는 LD_LIBRAYR_PATH와 LD_PRELOAD가 무시된다.

또한 애플에서 보안이 안좋은 환경변수가 있었던 적이 있다.
DYLD_PRINT_TO_FILE인데, 이건 Set-UID일 때 protected file에도 쓸 수 있고, 파일이 안닫히고 열려있을수도 있다.

저 파일 안에 etc/sudoers을 넣고, bob으로 계정을 전환한 후 해당 파일에 명령어를 쓰는 것이 가능하다
외부 프로그램 호출 통한 공격
애플리케이션이 외부 프로그램을 발생시킨다. 이 때 이들이 환경변수 등을 악용할 수 있다.
이 때 exec(), execve()을 쓰는 방식은 프로그램을 직접 실행한다
하지만 system()은 /bin/sh를 거치기 때문에 쉘을 거침 → 공격자 입장에서 더 취약
만약 쉘 프로그램이 절대경로 없이 외부경로를 호출받으면

이들은 PATH변수에서 cal 명령어을 찾는다.

공격자는 위 코드를 cal이라는 실행파일로 만든다.
/bin/dash는 루트쉘로 이동하게 한다.

.:$PATH는 현재 위치를 path 경로에 추가하는 것이다.
execve()를 사용하면 환경변수 영향을 받지 않고 쉘도 실행하지 않아서 만약 외부 프로그램이나 권한있는 프로그램을 사용할 떄에는 execve()를 사용하는 것이 좋다.
라이브러리 활용 공격
가끔 외부 라이브러리에서 함수를 가져온다. 이 때 이 함수가 환경변수를 사용하면 공격이 가능하다

이 프로그램은 gettext라는 외부 라이브러리 함수를 사용한다.
이 때 LANF, LC_ALL과 같은 환경변수를 사용하는데 이 환경변수는 유저에 의해 조작이 가능하다.
이 때 printf 취약점이 발생한다
이걸 방지하기 위해서는 라이브러리 개발자가 대응해야한다.
이 때 NSPATH 환경변수를 Set-UID일 때 무시하게 한다.
Application Code를 사용한 공격
만약 프로그램이 환경변수를 바로 사용하고, 이 때 권한이 높다면 이 프로그램은 위험할 수 있다.

여기서 환경변수 PWD를 직접 읽으면 위험하다

이 프로그램은 PWD를 알기 위해 getenv()를 사용한다.
이 때 pwd 값이 길면 오버플로우가 발생할 수 있다.
또한 pwd는 쉘에 의해 갱신되서 우기가 임의로 pwd값을 변경할수도 있다.
방지하기 위해서는
Set-UID상황에서 더 조심해야한다.
getenv()를 쓰는 대신에 secure_getenv()를 써야한다. 이걸 쓰면 euid와 ruid가 같지 않으면 널을 반환한다.
Set-UID Approch VS Service Approach

setUid는 권한이 바뀌면서 환경변수가 상속되어 위험하다.
하지만 service approach는 독립적 실행이라 신뢰가 가능하다.
setUid : 일반 유저가 특정한 프로그램을 root 권한으로 실행
service approach : 별도의 서비스에 요청을 보내고 그 서비스가 그 역할을 대시 수행한다.
setuid에서는 환경변수를 신뢰할 수 없기에 위험하다.
그래서 service 가 더 안전하다
SHELLSHOCK ATTACK
쉘이란? 운영체제 명령어 해석기이다.
유저와 os를 연결해준다.
sh, bash 등이 있고 문법이 조금씩 다르다
shell 함수
쉘쇽의 취약점은 쉘함수에 있다.

foo()는 쉘 함수를 선언하는 역할을 한다.
declare -f는 함수가 어떻게 정의되었는지 말해준다
foo는 함수를 실행하는 것이고, unset는 함수를 삭제하는 명령어이다.

이건 함수를 export 하는 것이다. 이렇게 하면 환경변수가 되어서 자식프로세스에도 전달되게 된다.
함수가 환경변수가 되면 자식프로세스에서 정의된다.

이런 방식으로 선언할수도 있다.
foo변수 안에 함수정의를 의미하는 string을 넣는다. 이 때 이 내부에서는 () {형식이면 함수라고 인식한다.
이 때 함수 foo 정의를 보려고해도 못본다.(현재 foo는 그냥 문자열변수이므로)
그런데 이 때 export를 하면 foo가 함수로 변환된다.
따라서 이 때는 함수 선언을 조회하면 함수 정의가 나오고, 그냥 문자열 변수를 출력하고자하면 아무것도 나오지 않는다.
먼저 함수를 환경변수로 내보내고, bash 명령을 함으로서 자식이 실행되면, 함수 정의로 변환된다.
또한 부모쉘이 아니라도 환경변수만 잘 넘기면 bash가 알아서 함수로 등록한다.
env BASH_FUNC_foo%%='() { echo hello; }' bash 이런식으로
Shellshock Vulnerability
이것은 환경변수에서 함수 전환할 때 취약하다
bash는 변수에 포함된 일부 명령어를 실행한다.

이렇게 함수 뒤에 다른 명령어도 넣으면 bash는 그 명령어도 실행한다.

이게 취약한 이유는 문자열이 (){로만 시작하면 바로 전체를 실행해버리기 때문이다.
() { 로 시작하면 bash는 = 를 스페이스바로 변환한다.
그 다음 문자열을 실행하는데 이 때 함수 정의 뿐만 아니라 다른 명령어로 실행한다.

A가 B가 된다.
B는 두개의 명령어를 가지게 된다.
이 때 이를 실행하면 두개의 명령어가 모두 실행된다.
만약 공격대상이 권한이 있는 상태에서 실행한다면 큰 보안문제가 발생할 수 있다.

bash가 실행되고, 신뢰 불가능한 유저의 input이 파싱될 때 shellshock가 발생한다.
취약한 SetUID Program

이 프로그램은 system을 사용해서 /bin/ls 명령어를 실행한다.
일반 사용자가 실행해도 루트 권한으로 동작한다
system 함수는 fork()를 사용해서 자식프로세스를 만드록, execl()로 /bin/sh 를 실행한다.
그래서 내부적으로 /bin/sh -c “ls -l” 이런식으로 실행되서 취약하다

일단 이 명령어로 취약한 쉘을 만든다.

그 다음 Set-UID로 만들고, 공격자가 환경변수를 설정한다. 이 때 쉘 명령이 포함되어 set-Uid프로그램이 실행된다
이러면 내부에서는
/bin/sh -c "ls -l”를 띄우고, 그 다음 함수를 파싱한다. 이 과정에서 /bin/sh가 실행되면서 루트쉘을 얻을 수 있게된다.
앞에서는 명령어 실행용 루트쉘을 받았고 뒤에 /bin/sh에서 대화용 루트쉘을 받게 되는 것이다.
저 앞에 system(”bin/ls -l”)은 그냥 예시일 뿐 system을 사용하면 bin/sh가 실행되고, 그 결과로 환경변수에 있던 함수 문자열들이 함수가 되는 과정에서 공격을 한다는 것이 중요하다
CGI Programs
이는 서버가 동적으로 웹페이지를 실행할 때 사용된다.
cgi는 쉘스크립트를 사용한다.
bash스크립트로 test.cgi라는 cgi 프로그램을 만든다

이 때 여기 접속하면

이렇게 작동된다.



공격자는 cgi로 요청을 보내는데 여기서 헤더로 온 값은 환경변수로 변환된다.

이런식으로 헤더를 설정하면 /bin/ls -l이 실행된다.
echo content_type를 넣는 것은 정상적인 cgi 프로그램처럼 보이게 하려고 하는 것이다.
cgi프로그램은 맨 위에 첫줄이 반드시 http 헤더여야하고, 그 아래 한줄이 있어야 실제 콘텐츠가 시작된다.
- 비밀번호 훔치기
보통 비밀번호는 설정파일에 하드코딩 되어있다.

그래서 이와 같이 cat 명령어(출력)을 사용해서 웹 서버의 내부 설정파일을 읽을 수 있다.
Reverse Shell 만들기
쉘 프로그램을 실행하고자 한다. 그런데 /bin/bash하면 실행은 되는데 조작은 안된다. 이를 보완하기 위해서 reverse shell을 만든다.
이 쉘은 input, output, error을 네트워크를 통해 전달한다
이렇게 하면 결과를 바로바로 출력할 수 있고, bash의 입출력을 공격자와의 네트워크로 연결하고, pc명령을 입력하면 이를 서버가 실행해서 다시 pc로 전송하도록 한다.
우리는 우리 컴퓨터에서 nc를 활용하여 대기한다.


서버에서 이런 명령어를 치면 우리가 nc를 활용하여 대기하는 pc와 연결할 수 있다.
-i는 대화형으로 하겠다는 이야기이고, dev/tcp/ip주소는 여기로 출력을 보내겠다는 의미이다.
또한 0<&1은 표준 입력도 여기서 받겠다는 의미이고, 2>&1은 오류도 받겠다는 의미이다.

그래서 이와 같이 헤더를 통해 해당 쉘에 컴퓨터와 연결하는 명령어를 입력하면 reverse shell이 만들어진다.
'CSE > 네트워크 및 웹보안' 카테고리의 다른 글
[웹보안] SSRF (1) | 2025.06.02 |
---|---|
[웹보안] ClickJacking Attack (1) | 2025.06.02 |
[웹보안] SQL Injection Attack (1) | 2025.06.02 |
[웹보안] XSS (4) | 2025.04.28 |
[웹보안] CSRF (0) | 2025.04.28 |