필터링을 빡빡하게 바꿨다.
/을 필터링하게 된게 좀 크다. 경로, /bin/cat을 접근하기가 쉽지 않아졌다.
void delete_env(){
char** p;
for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
}
거기다가 환경 변수에 접근해서 순회하며 전부다 0으로 하기 때문에 /bin 경로 안에 있는 cat에 접근하는 것도 불가능하다.

그런데 이상한 점은 echo 역시 하나의 파일이거 which 명령어로 위치를 확인해보면
/usr/bin/echo에 위차한다는 것이다. 그런데 echo 명령어는 잘만 동작한다.
ls나 cat은 안되는데 왜 echo만 되는 것인지 알아보았더니

echo는 shell 내부에 내장된 (builtin) 상태로도 존재하고, ls는 그렇지 않았다.
쉘에서 echo를 호출하면 우선순위에 따라 builtin을 먼저 실행한다고 한다.
쉘 역시 커널과 유저가 소통하기 위한 하나의 프로그램이고, 쉘 안에 echo가 하나의 함수처럼 구현돼 있어 있는것이다. fork 없이 동작해서 빠르고 리소스를 적게 쓴다.
그 중에서 read와 $를 이용해서 flag를 출력할 수 있는 커맨드를 입력할 수 있을 것 같다.

우선 \a는 cmd1에서와 같이 "" 안에 입력했을 때 일부 문자에 대한 escpae만 처리하기에 a가 뒤에 있기에 그대로 리터럴 문자 \가 들어간다. 따라서 stsrstr은 통과함과 동시에 argv[1]에는 사실상 flag로 인식할 수 있게 만든다.
<로 fd = 0을 fl\ag 파일의 내용으로 읽어 x에 저장하고, echo $x를 하면 최종적으로 flag의 내용을 출력할 수 있게 할 수 있다.
./cmd2 "read x < fl\ag; echo \$x
$역시 리터럴 문자로 인식되도록 \를 붙였고, strstr 필터링을 통과함과 동시에 flag를 출력할 수 있따.
./cmd2 "command -p cat fla*"
cmd1처럼 다른 풀이를 확인해보면,
command 라는 builtin 명령어를 활용하여 문제를 풀 수도 있었다.
command 명령어는 -p 옵션을 주면 PATH든 뭐든 환경 변수 관련 조건을 싹다 무시하고 POSIX가 정의한 표준 시스템 디렉토리 \bin, \usr\bin 에 있는 명령어를 실행하도록 강제하는 기능이라고 한다.
이건 모르면 ㄹㅇ 모를거 같은 풀이이긴 한데 도움이 많이 될 것 같다. 의도치 않은 PATH 덮어쓰기가 발생해도 안전하게 기본 명령어를 호출하는 용도로 쓰이는 것 같다.
cd / && /home/cmd2/cmd2 '$(pwd)"bin"$(pwd)cat $(pwd)"home"$(pwd)"cmd2"$(pwd)"fl""ag"'
요거는 cmd2를 호출하면서 안에 넣는 문자열은 아니다.
이는 root directory / 에서 pwd를 하면 / 필터링을 우회하면서도 / 문자를 쓸 수 있는 방법이다. 이걸 통해서 /를 모두 (pwd)로 대체하면, /bin/cat도 실행할 수 있고 flag의 절대 경로까지 안정적으로 입력이 가능하다. 개천재네
./cmd2 '$(echo "\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147")'
요거는 필터링을 우회하되 명령어가 실행되는 시점에 echo에 의해 명령어가 조립되는 방법이다.
-e 옵션을 붙이면 escpae 문자로 ascii 형식으로 해석한다고 한다.
C-style octal escape라고 하는데 \ 뒤에 최대 3자리의 8진수를 붙여서 하나의 바이트 값으로 해석하는 거라고 한다.
mkdir /tmp/ca
ln -s /bin/cat /tmp/cat
cd /tmp/ca
ln -s /home/cmd2/flag f
/home/cmd2/cmd2 "\${PWD}t f"
이거는... cat을 억지로 사용할려고 /tmp/ca 디렉토리를 만들고 안에 들어가, pwd와 't' 문자를 조합해 /tmp/cat을 호출하도록 한 것 같다. 위의 root에서 pwd를 쓴거를 좀 더 복잡하게 사용한 것 같긴 하지만 좋은 방법이다
/home/cmd2/cmd2 'set -s'
/bin/cat /home/cmd2/flag
set -s 명령어는 shell에서 표준 입력으로 명령어를 읽도록 설정하는 옵션으로 그냥 원하는 명령어를 마음대로 입력할 수 있다. 이거도 builtin 명령어이며, 원래는 명령 줄 인자 (cmd 창)에서 한 번에 명령어를 받지만 필터링을 통과해서 system 함수로 새로 띄운 shell이 interactive mode로 입력을 받아 명령을 전부다 받아들이게 된다. 그냥 system 함수로 shell 하나를 더 띄운거라고 보면 된다.
./cmd2 '$(printf "%b%c%c%c%b%c%c%c%b%b%b%c%c%c%c" "\57" "b" "i" "n" "\57" "c" "a" "t" "\40" "\56" "\57" "f" "l" "a" "g")'
./cmd2 "\$(printf '%bbin%bcat %blag\n' '\057' '\057' '\146')"
printf 함수 안에서는 %b format 형식으로 인자로 넘긴 escape문자를 바탕으로 숫자를 문자로 해석할 수 있기 때문에 /, = 전부 \57 같은거로 우회할 수 있고 %c 문자로 bin, cat, flag를 전부 조합해서 최종적으로 /bin/cat ./flag를 조합한 것이다.
물론 %b 포맷 스트링으로 문자 역시 해석하여 치환할 수 있다.
다른 라업 중에서는 %o 옵션을 통해서 hex를 8진수로 변환하여 조립 과정을 이중으로 한 것도 존재했다. 익숙한 16진수 ascii code를 c style octal escpae로 변환하는 과정이 한 번 더 들어간 것이다.
~/cmd2 "while read line; do echo \$line; done < 'f'lag"
while-do 문을 실행하여 flag의 내용을 출력하고, flag는 'f'lag로 우회했다.
전체 파일 내용을 한 줄 씩 읽고 출력할 수 있다.
./cmd2 "\${PWD%%h*}bin\${PWD%%h*}cat fla*"
%%는 bash에 존재하는 매개 변수 확장 기능중 하나로 변수 값 뒤쪽에서 패턴에 매치되는 부분을 가장 길게 제거하라는 것이다.
현재 pwd가 /home/cmd2이기 때문에 h부터 시작하는 패턴을 전부 지워 /만 남긴 것으로, pwd를 쓰지 않아도 /를 파싱할 수 있다는 걸 알수 있었다. 좋은 방법인듯
./cmd2 "read input; eval '\$input'"
eval 역시 builtin 명령어로, 문자열을 한 번 더 파싱해서 실행해주는 역할을 한다.
여기서 read를 먼저 호출해서 필터링이 없는 입력을 한 번 할 수 있어 그걸 통해서 우회할 수 있다.

Shell_variables_can_be_quite_fun_to_play_with!