Search

[rootme] Flash - Authentication

Tags
writeup
date
2022/01/07 12:50
Property
2022/01/07 12:50

1. 문제

1) 문제 확인
어떤 flash가 실행된다. 버튼을 이것저것 눌러보다보면, 인증 실패라고 뜬다. 해당 문제 referrence를 보면 action script라고 하는게 있다고한다.
Flash : 웹에서 동적인 컨텐츠를 보여주게끔 하는 툴
ActionScript : Flash의 동적인 컨텐츠를 제어하기 위해서 만들어지고 vm 환경에서 돌아가는 스크립트 언어
라고 한다. 일단 소스코들 살펴보자
<object> 태그 안에있는 코드가 action script인 것같다. RootMe.swf flash 파일을 삽입하는 것으로 보인다. 그리고 밑에 l1 함수가 존재하는데, value 값으로 들어온 값과 "db..." 를 비교한다. 인자로 들어온 값이 저 값과 일치해야 하는것으로 보인다.

2. 접근방법

SWF
SWF는 어도비 플래시에서 멀티미디어, 벡터 그래픽, 액션스크립트 등을 처리하는 데 사용하는 파일 형식이다. 현재 웹상의 벡터 그래픽 애니메이션 영역에서 지배적인 위치를 차지하고 있으며 액션스크립트를 사용하는 브라우저 게임에도 흔히 사용된다. 여기서 중요한 부분은 "액션스크립트" 입니다. SWF 파일 내에서 액션 스크립트를 통해 미디어 이상의 기능을 가질 수 있기 때문에 취약한 SWF를 통해 XSS나 URL Redirection 등 공격에 사용할 수 있습니다. * swf에서도 js 내 삽입되는 XSS와 똑같이 내부 흐름을 바꾼 후 스크립트 구문을 이용하여 XSS가 가능합니다.
Markdown
복사
대충 느낌 알겠다. 우선 SWF 디컴파일러로 저 파일을 분석해보자

3. 풀이

디컴파일러로 swf 파일을 열면 다음과 같이 actionScript 코드를 확인할수 있다. 코드 흐름을 파악해보자.
package { import flash.display.Loader; import flash.display.Sprite; import flash.utils.ByteArray; public class RootMe extends Sprite { private static const KEY:String = "rootmeifyoucan"; private const EmbeddedSWF:Class = RootMe_EmbeddedSWF; public function RootMe() { var _loc2_:Loader = null; super(); var _loc1_:ByteArray = new this.EmbeddedSWF(); if(_loc1_.length != 0) { XOR(_loc1_,KEY); _loc2_ = new Loader(); _loc2_.loadBytes(_loc1_); addChild(_loc2_); } } private static function XOR(param1:ByteArray, param2:String) : void { var _loc3_:Number = 0; var _loc4_:Number = 0; while(_loc4_ < param1.length) { param1[_loc4_] = param1[_loc4_] ^ param2.charCodeAt(_loc3_); _loc3_++; if(_loc3_ >= param2.length) { _loc3_ = 0; } _loc4_++; } } } }
JavaScript
복사
RootMe() 함수를 보면, RootMe_EmbeddedSWF 라는 걸 가져오고, 그 이름으로 객체를 하나 만든다. 그다음 해당 객체와 key 값을 인자로 XOR 함수를 호출한다.
XOR() 함수에서 인자로 들어온 key 값으로 문자 하나하나씩 xor 연산을 진행시킨다. 그다음 xor 연산된 결과값을 addchild()로 표시 컨테이너에 로드를 시키면 실제 swf 플래시 이미지가 로딩되는 것 같다.
현재 키 값이 그대로 보여지므로, 파이썬으로 바이너리 파일을 xor연산 시켜보자
test.py
f=open("./1_RootMe_EmbeddedSWF.bin","rb") f2=open("./test.bin","wb") key="rootmeifyoucan" string=f.read() data=[] for i in range(0,len(string),len(key)): data.append(string[i:i+len(key)]) for j in data: for i in range(0,len(j)): f2.write(chr(int(j[i].encode('hex'),16)^ord(key[i]))) f2.close() f.close()
JavaScript
복사
해당 코드로 xor 연산을 시키면, test.bin 바이너리가 생성되는데, 해당 바이너리는 swf파일이다. 이걸 다시 디컴파일러에 넣어보자
요렇게 Main3 이라는 actionscript가 나온다. 해당 코드에서 실제 버튼을 눌렀을때에 대한 로직이 수행되는 것같다. 잘 보면, _1pFaN(param1:MouseEvent) 마우스 이벤트 부분이 보인다. 1, 2, 3, 4 버튼을 클릭했을때의 로직이 여기 부분이다.
switch case 가 4개 존재하는 것으로 보아 순서대로 1 , 2, 3, 4 버튼동작 로직이다. 중요 로직만 다시 살펴보자
... this._9AkQo = 11266775; this._YrqkY = 11146309; this._Q371e = 8049718; this._CKDjY = 7884889; ... private function _1pFaN(param1:MouseEvent) : void { var _loc3_:uint = 0; var _loc2_:String = new String(""); switch(param1.currentTarget) { case this._ltn9P: _loc2_ = (this._9AkQo >> 16 & 255).toString(16).toUpperCase(); this._e4G79 = this._e4G79 + _loc2_; break; case this._akkO7: _loc2_ = (this._YrqkY >> 8 & 255).toString(16).toUpperCase(); this._e4G79 = this._e4G79 + _loc2_; break; case this._61MyZ: _loc2_ = (this._CKDjY & 255).toString(16).toUpperCase(); this._e4G79 = this._e4G79 + _loc2_; break; case this._vHNiT: if(this._e4G79.length > 1) { this._e4G79 = this._e4G79.slice(0,-1); } } var _loc4_:MD5 = new MD5(); var _loc5_:ByteArray = Hex.toArray(Hex.fromString(this._e4G79.split("").reverse().join(""))); var _loc6_:ByteArray = _loc4_.hash(_loc5_);
JavaScript
복사
case 문의 어떤게 1,2,3,4 인지에 대한 정보는 위에서
여기서 확인 할 수 있다. 좌표평면의 x,y,width,height 로 표시되는 graphics.drawRect 함수로 설정되어 있다.
따라서 첫번째 case로 예를 들면 _ltn9P 에 해당하는 값이 클릭됬을때
_loc2_ = (this._9AkQo >> 16 & 255).toString(16).toUpperCase()
JavaScript
복사
요 로직이 수행된다. _9AKQo 값은 위에 선언된 값이고, 그 값에서 쉬프트, and 연산이 진행된다.
그다음 switch case가 끝나고, _e4G79 요 값에 계산된 값들이 붙여진다. 그다음, reverse() 함수로 문자열을 뒤집은다음 md5()로 해싱을 진행한다. 여기서 해싱된 값이 js 코드에서 dbbcd6ee441aa6d2889e6e3cae6adebe 요값이 나오면 되는 것같다.
그렇다면 어떤 값이 해싱되서 dbbcd6ee441aa6d2889e6e3cae6adebe 요게 나오는지 파이썬코드로 switch case 로직을 짜보자
우선 저 해싱 값을 복호화 해보자.
4141955195AA 라는 값이 나온다.
예시로 저렇게 10203040 이라는 문자열을 문자 하나씩 split한뒤에, reverse를 하므로, 최종적으로 버튼 입력시에 AA5915591414 라는 문자열이 나와야한다.
on=11266775 tw=11146309 thre=7884889 test="" def one(): global test print("1 button :: "+hex(on>>16 & 255)) test=test+hex(on>>16&255)[2:] def two(): global test print("2 button :: "+hex(tw>>8&255)) test=test+hex(tw>>8&255)[2:] def three(): global test print("3 button :: "+hex(thre&255)) test=test+hex(thre&255)[2:] def four(): global test test=test[:-1] while(1): temp=input(": ") if temp==1: one() elif temp==2: two() elif temp==3: three() elif temp==4: four() print(test)
JavaScript
복사
요렇게 코드를 짠뒤에 AA5915591414 요게 나오게끔 입력해보자.
최종적으로
1, 4, 1, 4, 3, 2, 4, 3, 4, 3, 2, 2
요렇게 버튼을 누르면 된다. 한번 확인 해보자.
저 순서대로 버튼을 누르면 인증이 성공한다고 나온다. 입력한 숫자를 플래그로 입력해보자
성공

4. 몰랐던 개념

ActionScript
JPEX flash decompiler