🗂️ INDEX

글 작성자: Universe7202

🚪 Intro

웹 3번째 문제 ppower 입니다. 이 문제는 `prototype pollution` 을 통한 RCE를 하는 문제입니다.

 

💡 Analysis - 코드 분석

/answer 페이지로 GET 방식의 데이터를 전달할 수 있습니다. 이는 `req.query` 에 저장되여 `merge()` 함수를 통해 `r` 변수에 저장되는데, `merge()` 함수는 어떠한 검증없이 merge 하고 있습니다. 전형적인 prototype pollution 공격에  취약한 코드 입니다. 또한, 53번째 줄에서 `config.flagForEveryone` 값이 무조건 false가 아니어야 합니다. 그래야 `sendFlag()` 함수가 실행되죠.

 

 

`sendFlag()` 함수를 보면, 25번째 줄에서 `childProcess` 라는 함수를 통해 실행된 결과가 저장됩니다. 하지만, 30번째 줄에서 리턴하고 있는 값은 25번째 줄에 선언된 변수와 다른 변수입니다. (마지막에 g 문자 하나가 빠져있음...)

즉, `config.flagForEveryone` 값을 prototype pollution 공격을 통해 true 로 바꾼다고 해도 flag 값을 얻을 수 없습니다.

 

 

 

 

💡 Analysis - childProcess.execSync

대회가 끝난 이후 2개의 풀이를 보았는데, 이 둘의 풀이 과정 중 공통점이 있었습니다.

바로 `childProcess.execSync` 함수의 인자들 속성을 이용한 것인데요.

 

이 함수의 공식 문서를 보면, `command`는 필수적으로 넘겨야 하는 인자이며, 사용자는 선택적으로 `options` 값을 넘겨 줄 수 있습니다. 이 `options` 인자는 아래 공식 문서를 보면 알 수 있듯이, 다양한 옵션들이 존재 합니다.

https://nodejs.org/api/child_process.html#child_processexecsynccommand-options

 

위 `options` 인자들 중, `input`과 `shell` 이라는 옵션이 있습니다.

우선 `shell` 옵션에 대한 설명은 다음과 같습니다.

이 옵션은 명령을 실행할 쉘 입니다. 즉, Unix는 기본 쉘이 /bin/sh 이 default로 설정되어 있습니다. 사용자가 지정하지 않아도 말이죠. 그래서 `execSync` 함수에 첫번째 인자인 `command` 값을 넘겨주면, unix 기준으로 `/bin/sh command` 이렇게 실행이 됩니다.

 

그 다음으로, `input` 옵션에 대한 설명입니다.

이 옵션은 생성된 프로세스에 stdin으로 전달될 값입니다.

 

위 두가지 옵션과 prototype pollution 공격을 통해 RCE를 할 수 있습니다.

그 전에 추가로 설명할 것이 있습니다.

 

 

 

💡 Analysis - /sbin/debugfs

풀이를 보면서 이런 것도 있구나... 를 알게 되었어요.

`/sbin/debugfs` 는 ext2/ext3/ext4 파일시스템 디버거 프로그램 입니다.

이를 실행 후, 아래 사진처럼 !command 를 입력하면 명렬어를 실행할 수 있습니다.

위 동작 과정을 보면, `/sbin/debugfs` 를 실행 후 stdin 방식으로 입력을 받고 있습니다.

이를 이용하여 RCE를 해보겠습니다.

 

 

 

💡 Exploit - Prototype Pollution

우선, 52번째 줄에서 if 문 통과를 위해 조건에 맞는 인자를 넘겨줘야 합니다. 이후, `config.flagForEveryone` 값을 prototype pollution 을 이용하여 true로 바꿔야 합니다. 대표적으로 `__proto__` 를 이용하지만, 최신 node에서는 필터링 하는 것 같습니다.(?) 

따라서 prototype을 이용하여 pollution을 일으킬 수 있습니다. 이렇게 하면, flagForEveryone 의 값을 1로 설정할 수 있습니다.

/answer?constructor[prototype][flagForEveryone]=1&answer=It%27s-none-of-your-business

 

그 다음으로, `child_process.execSync()` 함수에 사용되는 `shell` 과 `input` 을 오염시킬 것입니다. 다음과 같은 payload를 사용합니다. 

constructor[prototype][shell]=/sbin/debugfs
constructor[prototype][input]=!id

`shell` 을 `/sbin/debugfs` 로 오염시키는 이유는, `shell`은 명령어를 실행할 쉘 입니다. 기본으로 /bin/sh로 설정되어 있지만, `/sbin/debugfs`로 오염시키게 되면 `/sbin/debugfs command` 이렇게 실행됩니다. 실제 터미널에서 실행하면 아래 사진처럼 ls라는 파일을 open 하려고 했지만 파일이 없다는 에러를 띄우고 입력 창이 출력되는 것을 볼 수 있습니다.

 

사용자가 직접 입력해야 하는 상황이라면, `child_process.execSync()` 함수에서 앞서 설명했던 `options`의 `input` 옵션을 사용합니다.

 

예를들어 !id 라는 값을 전달하면 `child_process.execSync(command)` 함수를 통해 `/sbin/debugfs command` 명령어가 동작하고, 이때 stdin 으로 !id 라는 값이 `/sbin/debugfs` 로 전달되어 명령어가 실행됩니다.

 

테스트 환경에서 실행하여 콘솔 창을 보면 `id` 명령어가 실행된 것을 볼 수 있습니다. (debugfs 명령어 에러도 보이네요.)

 

따라서 최종 payload는 다음과 같습니다. input 값은 reverse shell 혹은 다른 명령어를 넣어서 하면 RCE가 가능합니다.

/answer?constructor[prototype][flagForEveryone]=1&constructor[prototype][shell]=/sbin/debugfs&constructor[prototype][input]=!id&answer=It%27s-none-of-your-business

 

`WACon{node**pp=rce/*:P*/}`

'🚩CTF' 카테고리의 다른 글

DiceCTF 2022 writeup  (0) 2022.07.25
[WACon 2022] yet_another_baby_web  (0) 2022.06.28
[WACon 2022] Kuncɛlan write up  (0) 2022.06.27
zer0pts ctf 2022 GitFile Explorer write up  (0) 2022.03.27
hayyim CTF 2022 writeup  (0) 2022.02.13