==Phrack Inc.== Volume 0x0c, Issue 0x40, Phile #0x0f of 0x11 =-----------------------------------------------------------------------= =-------------=[ Blind TCP/IP hijacking is still alive ]=---------------= =-----------------------------------------------------------------------= =-----------------------------------------------------------------------= =-------------------=[ By lkm ]=-----------------------= =-------------------=[ <lkm_at_phrack_dot_org> ]=-----------------------= =-----------------------------------------------------------------------= 편역 : poc@securityproof.net 번역과정에서오역및오타가발생할수있습니다. 엄밀한글읽기를원하시는분은원텍스트를읽으시기바랍니다. 참고로원문에정확하지않은영문들이사용되고있는부분들이있습니다. 저자가 native speaker 인지는확인을하지 않았습니다. 가장좋은소스는원문텍스트입니다.
--[ Contents 1 - Introduction 2 - Prerequisites 2.1 - A brief reminder on TCP 2.2 - The interest of IP ID 2.3 - List of informations to gather 3 - Attack description 3.1 - Finding the client-port 3.2 - Finding the server's SND.NEXT 3.3 - Finding the client's SND.NEXT 4 - Discussion 4.1 - Vulnerable systems 4.2 - Limitations 5 - Conclusion 6 - References
--[ 1 도입 TCP를가지고노는것 (blind spoofing/hijacking, 등...) 은 initials TCP sequence number(isn) 를추측가능했던 (64K 룰, 등...) 몇년전에는아주인기가있었다. 요즘은 ISN들이랜덤화가아주잘되어있어이제는공격이거의불가능한것처럼보인다. 이글에서는 blind TCP hijacking 공격을수행하는것이 [1] 1 에서처럼 ISN을생성하는역할을하는 PRNG를공격하지않고도요즘여전히가능하다는것을보여줄것이다. 다음시스템 ((Windows 2K, windows XP, 그리고 FreeBSD 4) 들에적용될수있는방법도제시할것이다. 이방법은구현하기에직관적이지는않지만그럼에도불구하고완전히가능하며, 모든취약한시스템에대해이공격을수행하는데성공적으로사용된툴을코딩했기때문이다. --[ 2 필수전제 이섹션에서는이글전체를이해하는데필요한정보를몇가지제시할것이다. ----[ 2.1 - TCP에대한간단한상기두개의호스트 ( 이글에서는앞으로각각 "client" 와 "server" 라고지칭할것임 ) 사이에 TCP 연결은 client-ip, server-ip, client-port, server-port에의해확인될수있다. 서버포트가 well known 포트라면클라이언트의포트는보통 1024-5000 범위일것이며, 운영체제에의해자동으로부여된다.( 예 : 어떤사람으로부터 freenode로의연결은 [ppp289.someisp.com, irc.freenode.net, 1207, 6667] 에의해나타날수있다.) TCP 연결상에서통신이이루어지면교환된 TCP 패킷헤더들은이정보들 ( 실제로, IP 헤더는출발지 / 목적지 IP 를 가지고있으며, 그리고 TCP 헤더는출발지 / 목적지포트를가지고있다 ) 을가지고있다. 각 TCP 패킷헤더는또한 sequence number(seq) 및 acknowledgement number(ack) 에대한필드도가지고있다. 연결에포함된이두호스트의각각은연결이확립될때무작위로 32비트 SEQ number를연산한다. 이초기 initial SEQ number는 ISN이라고부른다. 그런다음, 한호스트가 N 바이트의데이터로패킷을보낼때마다 SEQ 번호에 N을더한다. sender는외부로나가는각 TCP 패킷의 SEQ 필드에그의현재 SEQ를더한다. ACK 필드는다른호스트로부터다음예상 SEQ number로가득찬다. 각호스트들은그자신의다음 sequence number(snd.next) 를유지하고, 그리고다른호스트 (RCV.NEXT) 로부터다음예상 SEQ number를유지한다. 한가지예를들어확인해보자.( 간단히하기위해, 이연결이이미확립되고, 포트들이보이지않는다고생각해보자.) 1 역자주 : Strange Attractors and TCP/IP Sequence Number Analysis - One Year Later (http://lcamtuf.coredump.cx/newtcp/)
Client Server [SND.NEXT=1000] [SND.NEXT=2000] --[SEQ=1000, ACK=2000, size=20]-> [SND.NEXT=1020] [SND.NEXT=2000] <-[SEQ=2000, ACK=1020, size=50]-- [SND.NEXT=1020] [SND.NEXT=2050] --[SEQ=1020, ACK=2050, size=0]-> 위의예에서, 먼저클라이언트는 20바이트의데이터를보낸다. 그런다음, 서버는이데이터를승인하고 (ACK=1020), 같은패킷에자신의 50바이트의데이터를보낸다. 클라이언트가보낸마지막패킷은우리가 "simple ACK" 라고부를것이다. 그것은서버가보낸 50바이트의데이터를승인하지만어떤데이터도탑재하여 (data payload) 운반하지는않는다. "simple ACK" 는호스트가받은데이터를승인하지만아직전송할데이터를가지고있지않은곳에서사용된다. 분명히, 어떤잘형성된 "simple ACK" 패킷은무한루프로연결될것이기때문에, 승인되지않을것이다. 개념적으로, 각바이트는 sequence number를가지고있으며, 그것은 TCP 헤더필드에포함된 SEQ는첫바이트의 sequence number를나타낸다. 예를들어, 첫패킷의 20 바이트는 sequence number 1000..1019를가진다. TCP 는 "window" 의개념을정의함으로써흐름통제 (flow control) 메커니즘을구현한다. 각호스트는우리가 RCV.WND 라고부를 TCP window 사이즈 ( 각 TCP 연결에따라동적이고특수하며, TCP 패킷에선정되어있음 ) 를가지고 있다. 언제라도하나의호스트는 RCV.NXT 와 (RCV.NXT+RCV.WND-1) 사이에 sequence number 를가진바이트를받아들일것이다. 이메커니즘은그호스트에 " 전송중인 "(in transit) RCV.WND 바이트보다더많을수없다는것을언제라도 확인해준다. 연결의확립과해체 (teardown) 는 TCP 헤더의플래그에의해관리된다. 이글에서유일하게유용한플래그들은 SYN, ACK, 그리고 RST( 추가정보에대해서는 RFC793 를참고할것 ) 이다. SYN 와 ACK 플래그는연결확립에서다음과같이 사용된다 : Client Server
[client picks an ISN] [SND.NEXT=5000] [SND.NEXT=5001] --[flags=syn, SEQ=5000]--> [server picks an ISN] [SND.NEXT=9000] <-[flags=syn+ack, SEQ=9000, ACK=5001]-- [SND.NEXT=5001] [SND.NEXT=9001] --[flags=ack, SEQ=5001, ACK=9001]-->...connection established... 이확립동안각호스트들의 SND.NEXT는 1씩증가한다고언급할것이다. 그것은 sequence number가관련되어있는한 SYN 플래그가 ( 가상의 ) 1 바이트로카운트되기때문이다. 그래서, SYN 플래그세트를가진어떤패킷은 1+packet_data_size( 여기서데이터사이즈는 0임 ) 씩 SND.NEXT를증가시킨다. 독자들은또한 ACK 필드는옵션이라는것을목격할것이다. ACK 필드는비록관련은있지만 ACK 플래그와혼동해서는안된다. ACK 플래그는 ACK 필드가존재하면설정된다. ACK 플래그는확립된연결에속한패킷에만항상설정된다. RST 플래그는 ( 닫힌포트에연결을시도하는것과같은에러때문에발생하는 ) 연결을비정상적으로닫기위해 사용된다. ---- [ 2.2 - IP ID의흥미로운부분 IP 헤더는 IP fragmentation/reassembly 메커니즘에의해사용되는 16비트정수인 IP_ID라는플래그를가지고있다. 이수는어떤한호스트에의해보내진각 IP 패킷을유일한것으로나타내기위해필요하지만 fragmentation에의해변경되지않을것이다 ( 그래서, 같은패킷의프래그먼트들은같은 IP ID를가질것이다.). 이제, 독자들은 IP_ID가왜흥미로운지궁금해할것이다. 몇몇 TCP/IP 스택 (Windows 98, 2K, 및 XP를포함. 이스택들은전역변수 counter에 IP_ID를저장하고있으며, 그것은각보내진 IP 패킷으로단순하게증가한다.) 에는멋진 기능 을있다. 이것은공격자가어떤한호스트의 IP_ID 카운트를확인할수있게해주며 ( 예를들어, ping으로 ), 그래서그호스트가패킷을보낼때를안다. 예 : attacker Host
--[PING]-> <-[PING REPLY, IP_ID=1000]--... wait a little... --[PING]-> <-[PING REPLY, IP_ID=1010]-- <attacker> Uh oh, the Host sent 9 IP packets between my pings. 이테크닉은잘알려져있으며, 이미스텔스포트스캔 ([3] 2 와 [5] 3 ) 를실제로수행하기위해사용되어왔다. ----[ 2.3 수집할정보의목록 자, 기존의 TCP 연결을하이재킹하기위해무엇이필요한가? 먼저, 우리는클라이언트의 IP, 서버의 IP, 클라이언트포트, 서버의포트를알필요가있다. 이글에서클라이언트의 IP, 서버의 IP, 서버의포트는알고있는것으로가정할것이다. 어려운것은클라이언트의포트를탐지하는것인데, 왜냐하면클라이언트의포트는 OS에의해무작위로부여되기때문이다. 다음섹션에서우리는 IP_ID로어떻게클라이언트의포트를알아낼것인지살펴볼것이다. 우리가두가지방법 ( 서버로부터클라이언트에데이터를보내고, 서버로부터클라이언트에데이터를보내는것 ) 을 하이재킹할수있기를원한다면필요한다음것은서버와클라이언트의 sequence number 를알아내는것이다. 분명히, 가장흥미로운것은클라이언트의 sequence number이다. 왜냐하면, 그것은클라이언트에의해보내진것처럼보이는것을서버로데이터를보내게할수있기때문이다. 하지만이글의나머지부분에서는서버의 sequence number를먼저탐지할필요가있음을보여줄것인데, 이는클라이언트의 sequence number를탐지할필요가있기때문이다. --[ 3 공격방법기술 ( 記述 ) 이섹션에서는어떻게클라이언트의포트를확인하는지, 그런다음서버의 sequence number를, 그리고마지막으로클라이언트의 sequence number를확인하는방법에대해알아볼것이다. 우리는클라이언트의 OS가취약한 OS라고가정할것이다. 그서버는어떤 OS에서도실행될수있다. 2 http://insecure.org/nmap/idlescan.html 3 http://seclists.org/bugtraq/1998/dec/0079.html
----[ 3.1 - client-port 찾기우리는이미클라이언트와서버의 IP, 그리고서버의포트를알고있다고가정한다면, 주어진포트가정확한클라이언트의포트인지확인하는잘알려진방법이있다. 이를위해우리는 client-ip:guessed-client-port로부터 server-ip:server-port로 SYN 프래그가설정된 TCP 패킷을보낼수있다.( 우리는이테크닉이적용되도록하기위해스푸핑된 IP 패킷을보낼수있을필요가있다.) 다음은 guessed-client-port 가정확한클라이언트의포트가아닐경우우리가패킷을보냈을때일어나는것이다 : Attacker (masquerading as client) Server --[flags=syn, SEQ=1000]-> 실제클라이언트 <-[flags=syn+ack, SEQ=2000, ACK=1001]--... 실제클라이언트는이연결을하지않았으며, 그래서그것은 RST 로중단한다... --[flags=rst]-> 다음은 guessed-client-port 가정확한클라이언트의포트일경우우리가패킷을보냈을때일어나는것이다 : Attacker (masquerading as client) Server --[flags=syn, SEQ=1000]-> 실제클라이언트... 공격자가보낸 SYN 을받자마자서버는단순한 ACK 로응답한다... <-[flags=ack, SEQ=xxxx, ACK=yyyy]--... 클라이언트는단순한 ACK 의응답으로아무것도보내지않는다...
이제, 이모든것중에서중요한것은첫번째경우에서는클라이언트가패킷을하나보낸다는것이고, 두번째 경우에서는그렇지않다는것이다. 만약섹션 2.2 를주의깊게읽었다면이특별한것이클라이언트의 IP conunter 를 확인함으로써탐지될수있다는것을독자여러분들은알것이다. 그래서, guessed client-port 가정확한것인지확인하기위해우리가해야할모든것은다음과같다 : - 클라이언트로 PING을보낸다.(IP ID를주목 ) - 스푸핑된 SYN 패킷을보낸다. - 클라이언트로 PING을다시보낸다.( 새로운 IP ID를주목 ) - 추측한포트가정확한지확인하기위해두개의 IP ID를비교한다. 만약사람들이효율적인스캐너를만들고자한다면분명히많은어려움들이있는데, 특히두개의 PING 사이에서클라이언트가도중에패킷을전송할수있다는것과, 클라이언트와서버사이의잠복 (latency)( 정확하지않은추측의경우그의 RST 패킷을클라이언트가보낸후에 delay에영향을미침 ) 이라는사실이다. 효율적인 client-port 스캐너를코딩하는것은독자들의몫으로남겨둔다. 공격전에클라이언트와서버사이의잠복을측정하고실시간으로클라이언트의트래픽에자신을순응시키는우리의툴로클라이언트의포트를 3분이내에보통발견할수있다. ----[ 3.2 - 서버의 SND.NEXT 발견하기 이제우리는클라이언트의포트를알게되었기때문에서버의 SND.NEXT( 현재 sequence number) 를알필요가있다. 어떤한호스트가출발지및목적지 (source/destination) 포트가명확하게있는 TCP 패킷을받지만부정확한 seg 그리고 / 또는 ack 를받을때마다정확한 SEQ/ACK number 를가진간단한 ACK 를보내준다. 우리가이문제에대해 알아보기전에 RFC793 에정의된것처럼정확한 seq/ack 4 이무엇인지정의하자 : 정확한 SEQ 는패킷을받는호스트의 RCV.NEXT 와 (RCV.NEXT+RCV.WND-1) 사이에있는 SEQ 이다. 전형적으로 RCV.WND 는 아주큰번호 ( 최소한수십 kilobyte) 이다. 정확한 ACK 는 ACK 를받는호스트가이미보낸어떤것의 sequence number 에상응하는 ACK 이다. 즉, 어떤호스트에 의해받은패킷의 ACK 필드는그호스트자신의현재 SND.SEQ 보다낮거나동일해야한다. 그렇지않다면 ACK 는 유효하지않을것 ( 보내지지않은데이터를인정할수는없다!) 이다. 4 http://www.ietf.org/rfc/rfc793.txt
sequence number 공간이 " 순환적 (circular)" 이라는것을주목하는것이중요하다. 예를들어, ACK 의유효성을 확인하기위해받는호스트에의해사용되는조건은단순히 unsigned comparison "ACK <= receiver's SND.NEXT" 이 아니라 signed comparison "(ACK - receiver's SND.NEXT) <= 0" 이다. 자, 우리가서버의 SND.NEXT 를추측하는원래의문제로돌아가자. 서버에서클라이언트로잘못된 SEQ 또는 ACK 를 보낸다면클라이언트는 ACK 를반환할것이며, 만약옳다고추측한다면클라이언트는아무것도보내지않을것이라는 것을우리는알고있다. 클라이언트의포트를탐지하는것은 IP ID 로테스트할수있다. 만약 ack1-ack2 = 2^31와같은 ACK 체크공식을살펴보면, 만약두개의 ACK 값들 (ack1와 ack2이라고부르자 ) 을무작위로선택하면정확하게그들중의하나는유효할것이다. 예를들어, ack1=0와 ack2=2^31라고해보자. 만약실제 ACK가 1과 2^31 사이라면 ack2는받아들여질수있는 ack가될것이다. 만약실제 ACK가 0 또는 (2^32-1) 와 (2^31 + 1) 사이라면 ack1은받아들여질수있을것이다. 이것을고려해보면, 우리는서버의 SND.NEXT를찾아내기위해 sequence number 공간을더쉽게스캐닝할수있다. 각각의추측은두개의패킷을보내는것을포함하고있는데, 이때추측된서버의 SND.NEXT에설정된그것의 SEQ 필드와더불어패킷이보내진다. 첫패킷 (resp. second packet) 은 ack1(resp. ack2) 에설정된그것의 ACK 필드를가질것이며, 그래서추측된것이 SND.NEXT이정확하다는것을확신할수있으며, 적어도그두패킷중에서하나는받아들여질수있을것이다. sequence number 공간은클라이언트포트공간보다더크지만, 두개의사실이이스캐닝을더쉽게만든다 : 먼저, 클라이언트가패킷을받으면, 그것은즉시응답한다. client-port 스캐닝에서처럼클라이언트와서버사이에잠복성을가진문제는없다. 그래서, 두개의 IP ID 확인사이의시간은아주작을수있으며, 스캐닝속도를올리고, 클라이언트가우리가확인하는것과탐지하는데바보짓을하는것사이에 IP 트래픽을가질가능성을아주많이줄일것이다. 두번째, 수신자의 window 때문에모든가능한 sequence number를테스트하는것이필요하지는않다. 사실, 최악의경우근사치 (2^32 / 클라이언트의 RCV.WND) 추측계산만필요하다 ( 이사실은이미 [6] 5 에서언급되었다.). 물론우리는클라이언트의 RCV.WND를알지못한다. 우리는 RCV.WND=64K라고대략추측하고, 스캐닝 (64K 배수로 SEQ를시도하며 ) 을할수있다. 그런다음, 만약우리가어떤것도발견하지못했다면모든 i에대해 seq = 32K + i*64k와같은모든 SEQ들을시도할수있다. 그런다음이미시도한 SEQ들을다시테스트하는것을피하며 seq=16k + i*32k와 5 http://osvdb.org/reference/slippinginthewindow_v1.0.doc
같은모든 SEQ 등을시도하며, window 를좁혀가는것이다. 전형적인 "modern" 연결에대해서, 이스캐닝은우리 툴로는 15 분보다적게걸린다. 알려진서버의 SND.NEXT로, 그리고 ACK를우리가모르는것을해결하는방법으로우리는 "server -> client" 식으로연결을하이재킹할수있다. 이것은나쁘지는않지만아주유용한것은아니라서, 우리가클라이언트에서서버로데이터를보내고, 클라이언트가명령을실행할수있도록할수있는것등을선호할수있다. 이를위해우리는클라이언트의 SND.NEXT를찾아낼필요가있다. ----[ 3.3 클라이언트의 SND.NEXT 찾기클라이언트의 SND.NEXT를알아내기위해무엇을할수있는가? 분명우리는서버의 SND.NEXT에했던것과같은방법을사용할수없는데, 이는서버의 OS가아마도이공격에취약하지않기때문이며, 게다가서버상의부담스러운네트워크트래픽은 IP ID 분석을실행불가능하게할수있다. 하지만, 우리는서버의 SND.NEXT를알고있다. 또한클라이언트의 SND.NEXT가클라이언트의내부로들어오는패킷의 ACK 필드를점검하는데사용된다는것을알고있다. 그래서우리는서버의 SND.NEXT에설정된 SEQ 필드로서버로부터클라이언트로패킷을보낼수있으며, ACK를가려내고, 그리고 ( 다시 IP ID로 ) ACK가받아들일수있는지확인할수있다. 만약 ACK 가받아들일수있는지탐지한다면이것은 (guessed_ack - SND.NEXT) <= 0 라는것을의미한다. 그렇지않다면 이미추측했을것같은데, (guessed_ack - SND_NEXT) > 0 임을의미한다. 이지식을이용하면, 우리는바이너리분석을통해약 32 번의시도로정확한 SND_NEXT 를알아낼수있다 (sequence space 가순환적이기때문에다소변경된것이다 ). 이제우리는마침내필요한모든정보를가지게되었으며, 우리는클라이언트나서버로어느쪽으로부터도세션 하이재킹 (session hijacking) 을할수있게되었다. --[ 4 토론 이섹션에서우리는취약한시스템을확인하고, 이공격의한계를토론하며, 더오래된시스템에대한비슷한공격을 제시할것이다.
----[ 4.1 취약한시스템이공격은 Windows 2K, Windows XP <= SP2, 그리고 FreeBSD 4에서테스트되었다. FreeBSD는 IP ID를랜덤화하는커널옵션을가지고있으며, 이것은이공격을불가능하게만든다는것을주목해야한다. 우리가알고있는한 Windows 2K와 XP에대한해결책은없다. 취약한시스템에서이공격을가능하게만드는유일한 " 버그 " 는비랜덤화된 IP ID 이다. 다른행위자 ( 우리가바이너리 분석등을가능하게하는 ACK 점검 ) 들은 RFC793 에의해예상된다 ( 하지만, [4] 6 에서이문제를개선시키기위한작업이 있었다 ). 우리가테스트를할수있었던한 Windows 2K, Windows XP, 그리고 FreeBSD 4가취약했다는것을확인하는것은흥미롭다. 같은 IP ID 증가시스템을사용하는다른 OS가있지만같은 ACk 점검메커니즘을사용하지는않는다. Windows와 FreeBSD의 TCP/IP 스택행위사이의유사성은골칫거리다. MacOS X는 FreeBSD에기반을두고있지만취약하지는않은데, 이것은 MacOS X가다른 IP ID 번호부여 (numbering) 구조를사용하기때문이다. Windows Vista는테스트를하지않았다. ----[ 4.2 한계들 앞에서기술한공격은다양한한계들을가지고있다 : 첫째, 이공격은 Windows 98에서작동되지않는다. 하지만실제한계는아닌데, 왜냐하면 Windows 98의초기 SEQ는 1,000분의 1초, 2^32 이하 (modulo 2^32) 내의머신의가동시간과동일하기때문이다. 우리는 Windows 98로어떻게하이재킹을하는지에대해토론하지않을것이다. 두번째, 만약클라이언트의연결이느리거나많은트래픽을가지면 (IP ID 분석으로바보같은짓을하며 ) 이공격은어려워질것이다. 또한, 클라이언트와서버사이의잠복성의문제가있다. 이문제들은잠복을측정하고, 호스트들이트래픽을가지는때를탐지하는똑똑한툴을만들어줄일수있다. 더나아가, 우리는클라이언트호스트에접근할필요가있다. 우리는패킷을보내고 IP ID 를가지기위해응답을받을 수있을필요가있다. ICMP 또는 TCP 또는그무엇이든어떤타입의패킷도괜찮다. 이공격은만약호스트가모든 타입의패킷을절대적으로막는방화벽 /NAT/... 등의뒤에있을경우가능하지않을것이다. 하지만필터링되지않는 6 http://www.ietf.org/internet-drafts/draft-ietf-tcpm-tcpsecure-07.txt
하나의포트만있어도이공격을가능하게만들수있기에충분하다. 이문제는통합된방화벽을가진 Windows XP SP2 와그이후에나온 OS 에도존재한다. Windows XP SP2 는취약하지만방화벽이어떤상황에서는공격을막을수 있으지모른다. --[ 5 결론이글에서우리는 Windows 2K/XP, 그리고 FreeBSD 4에서작동하는 blind TCP hijacking의방법한가지를제시했다. 이방법이많은한계를가지고있지만완벽하게가능하며, 많은호스트들에게적용될수있다. 더나아가, TCP 상의많은프로토콜들은여전히암호화되지않은통신을사용하며, 그래서 blind TCP hijacking의보안에대한영향을무시할수는없다. --[ 6 참고문헌 [1] http://lcamtuf.coredump.cx/newtcp/ [2] http://www.ietf.org/rfc/rfc793.txt [3] http://insecure.org/nmap/idlescan.html [4] http://www.ietf.org/internet-drafts/draft-ietf-tcpm-tcpsecure-07.txt [5] http://seclists.org/bugtraq/1998/dec/0079.html [6] http://osvdb.org/reference/slippinginthewindow_v1.0.doc