STL set, map iterator 의 문제점

Programming | 2009/05/30 12:59 | 오윤식
STL 의 iterator 에는 데이터 변경이 가능한 iterator 와 읽기만 가능한 const_iterator 가 있다.

set 과 map 도 마찬가지로 iterator const_iterator 가 있다.
하지만 다른 컨테이너들 (vector, list, deque, ..) 다른점은 set, map 은 데이타의 내용이 원소의 순서에 영향을 미친다는 것이다.

set, map 은 내부적으로 tree 로 구현되어 있다. 따라서 들어오는 원소에 따라서 트리의 구성이 달라지게 된다. 그렇기 때문에 set, map iterator 에는 다음과 같은 문제가 있다.

std::set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);

std::set<int>::iterator iter = s.begin();
*iter = 4;

assert(s.find(2) != s.end());

코드의 assert 구문은 보증할 수가 없다.
내부 구현에 따라 운이 좋게 케이스에 대해서는 assert 가 안 날 수도 있지만 분명히 잘 못된 코드이고 앞으로의 동작을 보증할 수 없게 된다. 내부적으로 구성해 놓은 트리의 값을 임의로 바꾸었기 때문이다.
(VC9 에서 테스트 해보면 assert 가 발생한다.)

그러므로 set, map 은 iterator 를 허용하면 안된다. const_iterator 허용해야 하는 것이다. 그래서 c++0x 부터는 set, map 의 iterator const iterator 로만 동작을 하도록 표준이 바뀌었다.

다행이도 gcc 같은 경우는 꽤 오래전부터 (정확한 버전은 모름) iterator 를 const_iterator 와 같게 처리 해 왔다. 따라서 위와 같은 코드는 다음과 같은 에러를 내게 된다.
error: assignment of read-only location ‘iter.std::_Rb_tree_const_iterator<_Tp>::operator* [with _Tp = int]()’

Visual C++ 도 10 (Visual Studio 2010) 부터는 위의 코드를 허용하지 않도록 바뀌었다. 따라서 다음과 같은 에러를 낸다.
error C3892: 'iter' : you cannot assign to a variable that is const

VC10 은 c++0x 의 문법이 대거 적용되었으므로 위의 문제도 함께 해결된 것이다.

만약 최신 컴파일러를 쓰지 않는다면 주의해야 겠으며, 가장 좋은 방법은 set, map 은 const_iterator 만을 쓰는 것이다.


SSL Strip sniffing

Security | 2009/05/16 13:08 | 오윤식
예전에 'dsniff 를 이용한 패킷 sniffing' 에서 SSL 의 경우 MITM 을 이용하여 sniffing 하는 방법을 적었었다. 하지만 이것은 사용자가 상위 기관에서 인증되지 않은 인증서를 사용 허가해야 가능한 방법으로 사용자가 주의하면 피할 수 있는 방법이다.

그래서 가능한 또 다른 방법이 SSL Strip 이다. 이것 역시 통신 중간에 끼어들어 실제 서버와의 정상적인 SSL 접속을 변경 시킨다는 것은 똑같다. (당연한 것이 정상적으로 SSL 통신을 한다면 중간에 아무리 끼어들어도 절대 내용을 볼 수 없다.)

하지만 기존 방법은 자신의 가짜 인증서를 보내서 클라이언트와 SSL 연결을 하도록 유도 하는 방법이지만, SSL Strip 은 아예 클라이언트가 SSL 접속을 쓰지 않도록 만드는 것이다.

예를 들어 웹페이지에서 전송되는 웹페이지의 링크 URL 주소들의 'https' 스트링을 'http' 로 바꾸어 전송하게 되면 사용자의 컴퓨터는 그 링크를 클릭 했을 때 SSL 을 쓰지 않는 일반 http 프로토콜로 접속을 하게 된다. 따라서 공격자는 전송되는 내용을 모두 볼 수 있으며 실제 서버와의 정상적인 통신을 위해서 실제 서버와는 SSL 연결을 하여 전달하게 된다.

사용자들가 주소창을 자세히 보지 않는 이상 연결이 SSL 을 쓰는 않는지를 알아채기 힘들며, 만약 안다고 해도 대부분의 사용자는 SSL 을 쓰지 않는다고 해도 별 생각없이 개인정보들을 입력하며 브라우징 할 것이다.

간단히 socat 과 netsed 명령을 조합하여 실습 해 볼 수 있다.
https://www.test.com 라는 사이트와의 통신을 가로챈다고 해보자.

1. 우선 예전에 설명했듯이 dsniff 툴을 이용하여 통신 중간에 끼어든다.

2. 클라이언트가 SSL 연결을 사용하지 않도록 해당 프로토콜 스트링을 바꾼다.
$ sudo netsed tcp 80 127.0.0.1 10000 's/https/http'

3. 실제 서버와는 SSL 을 통신을 해야 하므로 일반 TCP 연결을 SSL 연결로 바꿔준다.
$ socat tcp-l:10000,fork,reuseaddr openssl:www.test.com:443,verify=0

4. tcpdump 같은 프로그램을 이용하여 암호화 되지 않은 패킷을 볼 수 있다.

물론 이것은 단순히 특정 사이트만을 접속한다고 가정할때 가능한 것이고 실제 웹은 접속하는 사이트가 여러군데이며, https 를 쓰지 않는 사이트가 중간에 포함될 수 있으므로 완벽하게 하기 위해서는 그런 사이트들의 주소 관리가 필요할 것이다.

아마 프로그램을 만들어도 크게 어려움 없이 쉽게 작성할 수 있을 것이다. 이미 python 으로 작성된 sslstrip 프로그램이 있긴한데, 내가 잘 못 사용해서 그런지 테스트 해 본 결과 잘 작동하지 않았다. 나중에 시간되면 한번 간단히 만들어 볼 생각이다.


ssh 를 이용하여 UDP 터널링 하기

Software | 2009/04/12 01:00 | 오윤식
ssh 에는 터널링 기능이 있어서 암호화 되지 않은 패킷도 ssl 프로토콜 위에 올려 암호화여 중계 할 수 있다. ( http://en.wikipedia.org/wiki/Ssh_tunnel#SSH_tunneling )

이것은 보통 방화벽 등으로 막혀 있는 컴퓨터에서 외부와 통신하기 위해서도 유용하게 사용될 수 있다.

하지만 ssl 은 tcp 기반 이므로 기본적으로 tcp 패킷만 터널링 할 수 있다. 그렇지만 socat이용면 tcp 를 다시 udp 로 replay 해주는 방법으로 udp 프로토콜도 사용할 수 있다.

다음은 udp 터널링으로 dns 정보를 가져오는 예제이다.

192.168.0.2 에서 192.168.0.1 를 통해 168.126.63.1 (실제 DNS 서버) 로 패킷이 전달되게 되도록 설정하는 예이다.

ssh 터널링 (192.168.0.1)
$ ssh -R 10001:localhost:10002 192.168.0.2

udp 패킷으로 중계 (192.168.0.1)
$ socat tcp-l:10002,fork udp:168.126.63.1:53

udp 패킷에서 중계 (192.168.0.2)
# socat udp-l:53,fork tcp:localhost:10001

터널링 테스트 (192.168.0.2)
$ host www.google.com localhost
Server:         localhost
Address:        127.0.0.1#53

Non-authoritative answer:
www.google.com  canonical name = www.l.google.com.
Name:   www.l.google.com
Address: 74.125.127.104
Name:   www.l.google.com
Address: 74.125.127.99
Name:   www.l.google.com
Address: 74.125.127.103
Name:   www.l.google.com
Address: 74.125.127.147

이전 1 2 3 4 5 ... 12 다음