반응형

이 문서는 https://docs.google.com/document/d/1gY9-YNDNAB1eip-RTPbqphgySwSNSDHLq9D5Bty4FSU/edit 를 번역한 문서입니다. 오역 및 의역이 매우 많을 가능성이 있습니다. :)

QUIC란?

QUIC (Quick UDP Internet Connections) Google에서 개발한 새로운 인터넷 전송 프로토콜 입니다.

QUIC는 사용자들은 거의 또는 전혀 변경하지 않으면서, 최신 웹 애플리케이션에서 발생하는 수많은 전송 계층과 응용 계층 문제를 해결합니다. QUIC는 TCP+TLS+HTTP2와 비슷하지만 UDP위에 구현됩니다. 자체적으로 만들어진 프로토콜인 QUIC를 사용하면 레거시 클라이언트와 미들박스에 의해 방해받는 기존의 프로토콜로는 불가능한 혁신을 가능케 할 수 있습니다.

QUIC over TCP+TLS+HTTP2의 주요 이점들:

연결 설정 대기 시간
혼잡 제어 개선
Head-of-line blocking(HOL blocking)문제 없이 멀티플렉싱
전진 에러 수정(FEC)
연결 마이그레이션

연결 설정

연결 설정에 대해 자세히 알고 싶다면 QUIC Crypto design 문서를 참조하세요. 간단히, 페이로드를 보내기 위해 1~3번의 왕복이 필요한 TCP+TLS와 비교해서 QUIC의 handshake는 자주 왕복을 필요로 하지 않습니다.

처음으로 QUIC 클라이언트가 서버에 접속할때, 클라이언트는 반드시 handshake를 완료하는데 필요한 정보를 얻기 위해 1 왕복 handshake를 수행해야 합니다. 클라이언트가 inchoate(empty) client hello(CHLO)를 보내면, 서버는 소스 주소 토큰과 서버 인증서를 포함하여 클라이언트가 진행하는데 필요한 정보를 rejection(REJ)와 함께 보냅니다. 다음번에 클라이언트가 CHLO를 전송하면, 이전 연결의 캐시 된 자격 증명을 사용하여 암호화된 요청을 즉시 서버에 보낼 수 있습니다.

혼잡 제어

QUIC는 플러그형 혼잡 제어를 가지고 있으며, TCP보다 혼잡제어 알고리즘에 더 많은 정보를 제공합니다. 현재, Google의 QUIC는 TCP Cubic의 재 구현을 사용하고 있으며 대안적인 접근법을 실험하고 있습니다.

많은 정보의 예는 원본과 재전송된 각 패킷이 새로운 시퀸스 번호를 전달한다는 것입니다. 이를 통해 QUIC 전달자는 원본에 대한 ACK과 재전송을 위한 ACK을 구별할 수 있으며 TCP의 재전송 모호 문제를 피할 수 있습니다. 또한 QUIC ACK는 패킷 수신과 전송 확인 사이의 지연을 단조롭게 전달하고 단조롭게 증가하는 시퀀스 번호와 함께 전달합니다. 이를 통해 정확한 왕복 시간을 계산할 수 있습니다.

마지막으로, QUIC의 ACK 프레임은 최대 256 NACK 범위를 지원하므로, QUIC는 TCP(SACK포함)보다 순서 재 지정에 더 탄력적이며, 재 지정 또는 손실이 있을때 더 많은 바이트를 망에 보관 가능합니다. 클라이언트와 서버 모두 각자가 수신 한 패킷을 정확하게 파악할 수 있습니다.

멀티플렉싱

TCP+HTTP2가 갖는 큰 문제중 하나는 head-of-line blocking(HOL blocking) 문제입니다. 애플리케이션은 TCP 연결을 바이트 스트림으로 간주합니다. TCP 패킷이 손실되면, 해당 HTTP2 연결 스트림은 패킷이 재 전송되어 상대방이 수신 할 때까지 계속 진행할 수 없습니다. 이러한 스트림에 대한 데이터가있는 패킷이 도착하여 버퍼에서 대기중인 경우에도 마찬가지입니다.

QUIC는 멀티플렉싱 동작을 위해 처음부터 디자인 되었기 때문에, 개별 스트림에 대한 데이터를 가지고 있는 손실 패킷은 일반적으로 특정 스트림에만 영향을 미칩니다. 각 스트림 프레임은 도착시 해당 스트림으로 전달 할 수 있으므로, 손실이 없는 스트림은 재구성될 수 있고 애플리케이션에서 진행될 수 있습니다.

전진 에러 수정(FEC)

재전송을 기다리지 않고 손실된 패킷을 복구하기 위해, QUIC는 FEC 패킷으로 패킷 그룹을 보완할 수 있습니다. RAID-4와 유사하게 FEC 패킷은 FEC 그룹의 패킷들의 패리티를 포함합니다. 그룹 내의 패킷 중 하나가 손실되면, 그 패킷의 내용은 FCE 패킷과 그룹 내부에 남아있는 패킷들로부터 복구할 수 있습니다. 전송자는 특정 시나리오(예시: 요청의 시작과 끝)를 최적화하기 위해 FEC 패킷을 보낼지 여부를 결정할 수 있습니다.

연결 마이그레이션

QUIC 연결은 클라이언트에서 임의로 생성된 64bit 연결 ID로 식별됩니다. 반대로 TCP 연결은 송신 주소, 송신 포트, 수신 주소, 수신 포트라는 4개의 튜플로 식별됩니다. 이것은 만약 클라이언트의 IP주소가 바뀌거나(예시: Wi-Fi 범위를 벗어나서 데이터 모드로 전환)또는 포트(NAT box가 손실되어 포트를 리바인딩 하는 경우)가 바뀌는 경우 활성화 된 TCP 연결은 더 이상 유효하지 않습니다. QUIC 클라이언트가 IP를 변경하게 되면, 새로운 IP 주소에서 이전 연결 ID를 사용하여 진행중인 요청을 방해받지 않을 수 있습니다.

'Network > QUIC' 카테고리의 다른 글

QUIC FAQ  (0) 2017.06.03
반응형

아직도 생각하면 멘탈 나가는 문제

양자 통신 프로토콜 BB84를 구현한 문제이다.

https://ko.wikipedia.org/wiki/%EC%96%91%EC%9E%90%EC%95%94%ED%98%B8 : 위키백과

우리는 Alice와 Bob이 통신하는 것을 도청과 값을 바꿀 수 있는데, 이 때문에 Man in the Middle Attack이 가능하다.


일단 어떤 절차인지 알아보자

일단 이 프로그램은 편광필터(기저, Basis)를 Y와 Z로 구분했고 비트는 -1, 1이 존재한다.

1. Alice가 600개의 랜덤한 기저와 비트를 생성해서 Bob에게 보낸다. Bob은 Z기저 1을 보내어 ACK 확인을 한다.

2. Bob은 600개를 받는 즉시 기저를 Z에 맞추고, 추측한 600개의 기저를 Alice에게 Z로 추측했다면 -1을 Y로 추측했다면 1을 보내고, Alice는 Z기저로 추측한 기저가 맞다면 1을 틀리다면 -1을 보낸다.

3. Alice와 Bob은 도청이 있는지 확인하기 위해서 짝수번째 비트들을 Z기저를 통해 보낸다. Alice는 Z기저로 1을 보내어 ACK 확인을 한다.

4. 이후 Alice와 Bob은 도청이 없다고 확인한 뒤 홀수번째 비트들로 AES 128bit키를 만든다(-1 -> 0, 1 -> 1)

5. Alice와 Bob은 AES128-ECB 통신을 한다.


해석만 할 줄 알면 쉬운 문제였는데 문제는 통합 1500+a 통신을 하는 것을 감청하고 바꿔야 해서 

한번 시도 하는데 30분이 넘게 걸려서 멘탈이 파쇄당했다. 한번에 비트들을 보냈으면 더 좋지 않았을까 한다.



from pwn import *

r = remote("bb8.chal.pwning.xxx",20811)

def GetQubit():
    ZAq = []
    ZBq = []
    for i in range(599):
        print i
        r.sendlineafter("Bob, do you want to intercept (y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        K = r.recvuntil("\n")
        ZAq.append(int(K))
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(-1/1)","-1")
        ZBq.append(0)
        r.sendlineafter("(y/N)?","N")

    print "600"
    r.sendlineafter("Bob, do you want to intercept (y/N)?","y")
    r.sendlineafter("(Z/Y)","Z")
    r.recvuntil("measured ")
    K = r.recvuntil("\n")
    ZAq.append(int(K))
    r.sendlineafter("(Y)?","Y")
    r.sendlineafter("(Z/Y)","Z")
    ZBq.append(0)
    r.sendlineafter("(-1/1)","-1")
    return (ZAq,ZBq)

def WatchBases(ZAq,ZBq):
    CAq = []
    CBq = []
    C = 0
    T = 0
    for i in range(600):
        print i
        r.sendlineafter("(y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        B = r.recvuntil("\n")
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(-1/1)","-1")
        r.sendlineafter("(y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        K = r.recvuntil("\n")
        if int(K) == 1:
            CAq.append(ZAq[i])
            C+=1
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        if T <= 256:
            
            if int(B) == 1 or C == 0:
                r.sendlineafter("(-1/1)","-1")
            else:
                T+=1
                r.sendlineafter("(-1/1)","1")
                CBq.append(ZBq[i])
                C -= 1
        else:
            if C != 0:
                r.sendlineafter("(-1/1)","1")
                CBq.append(ZBq[i])
                C -= 1
            else:
                r.sendlineafter("(-1/1)","-1")
    return (CAq,CBq)

def Check(Cq):
    for i in range(0, len(Cq), 2):
        print i
        r.sendlineafter("(y/N)?", "y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(-1/1)",str(Cq[i]))
        r.sendlineafter("(y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        K = r.recvuntil("\n")
        if int(K) != 1:
            print "fuck!"
        r.sendlineafter("(Y)?","N")

def main():
    ZAq,ZBq = GetQubit()
    print ZAq
    print ZBq
    CAq,CBq = WatchBases(ZAq,ZBq)
    print CAq
    print CBq
    print len(CAq)
    print len(CBq)
    assert(len(CAq) == len(CBq))
    Check(CAq)

    KeybitsA = []
    KeybitsB = []

    for i in range(len(CAq)):
        if CAq[i] == -1:
            KeybitsA.append(0)
        else:
            KeybitsA.append(1)

    for i in range(len(CBq)):
        if CBq[i] == -1:
            KeybitsB.append(0)
        else:
            KeybitsB.append(1)

    print KeybitsA
    print KeybitsB
    aes_keyA = 0
    aes_keyB = 0
    for i in range(128):
        aes_keyA |= (KeybitsA[2*i+1] << (127-i))
        aes_keyB |= (KeybitsB[2*i+1] << (127-i))

    print "aes key A : " + hex(aes_keyA)
    print "aes key B : " + hex(aes_keyB)

    r.interactive()

if __name__ == "__main__":
    main()


'CTF' 카테고리의 다른 글

2017 DIMICTF problems & 후기  (0) 2017.07.22
Google CTF 2017 Moon  (0) 2017.06.23
SSG 2017 Write Up  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
CodeGate 2017 Event Challenge Keypad  (0) 2017.04.14
반응형

포너블 실력은 어디갔는지 보이지를 않네;;


SSGWriteUp.pdf


'CTF' 카테고리의 다른 글

Google CTF 2017 Moon  (0) 2017.06.23
Plaid CTF 2017 BB8  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
CodeGate 2017 Event Challenge Keypad  (0) 2017.04.14
DoubleS1405 Write UP  (0) 2017.04.01

+ Recent posts