Padding Oracle Attack for beginner2 -kknock.pdf
Padding Oracle Attack for beginner
K.knock
손민성
본 문서는 패딩오라클 어택에 전반적인 이해와 공부를 위해 작성되었으며,
본 문서를 이용하여 벌어지는 일에 대한 책임은 자신에게 있습니다.
오라클 패딩?
오라클 패딩 이라고 하면 oracle사를 생각하는 분들이 많을거라고 생각됩니다. 하지만 이 문서를 읽는 분들이 설마 그거라고 생각하고 읽지는 않으시겠죠?
여기서의 오라클은 암호학에서의 오라클을 의미합니다. 간단히 말하면 사용자와 시스템의 협조적인 관계를 통해 복호화 or 암호화를 진행하는 시스템을 의미합니다. 오라클 패딩은 암호학에서의 오라클의 한 종류로 볼 수 있습니다.
결론적으로 오라클 패딩이란 복호화 시스템에 암호문을 넣었을 때에 그에 대한 패딩의 올바름 유무를 보여주는 오라클을 말합니다.
블록 암호
오라클 패딩 공격에 대해 서술하기 전에 배경지식으로 가지고 있어야 할 몇가지가 있습니다. 그중 하나가 블록 암호 입니다.
블록 암호의 정의는 정말 간단합니다. 암호화 시키고 싶은 데이터를 일정한 크기의 byte로 잘라내어 각각을 블록으로 지정하고 그것을 앞에서부터 블록별로 암호화를 진행하는겁니다. 이해가 안되시면 아래 예제를 보여드릴께요.
데이터
0x01020304050607080910111213141516
가 있다고 가정해봅시다. 이를 4byte를 한
블록으로 하는 블록암호방식을 통하여 암호화를 진행한다면?(일반적으로는 8byte or 16byte를 한 블록으로 하는 경우가 많지만 설명의 편의를 위해 4byte로 하겠습니다)
0x01 |
0x02 |
0x03 |
0x04 |
0x05 |
0x06 |
0x07 |
0x08 |
0x09 |
0x10 |
0x11 |
0x12 |
0x13 |
0x14 |
0x15 |
0x16 |
<표 1>
위 표와 같이 데이터를 4바이트씩 끊어서 각 블록별로 암호화가 진행되게 됩니다.
그렇다면 만일 데이터가 위와같이 블록에 딱 맞아 떨어지지 않고 더 짧거나 하는 경우에는 어떻게 해야 할까요??
데이터
0x01020304050607080910111213
가 있다고 가정한 후에, 이를 4byte를 한
블록으로 하는 블록 암호방식을 통하여 암호화를 한다고 가정해봅시다.
0x01 |
0x02 |
0x03 |
0x04 |
0x05 |
0x06 |
0x07 |
0x08 |
0x09 |
0x10 |
0x11 |
0x12 |
0x13 |
??? |
??? |
??? |
<표 2>
뒤에 공간이 남아버리는군요! 그러면 그 공간에는 무엇을
넣어줘야 하죠??
이러한 경우를 해결하기 위해 우리는 패딩을 사용하게 됩니다.
패딩은 뭔데요?
그러면 패딩이 뭔지 궁금해 하실 겁니다.
패딩은 블록암호방식에서 사용되는 빈칸채우기용 값이라고 생각하시면 편합니다
표 2의 경우 뒤의 3바이트에 들어가게 될 어느
값을 패딩이라고 부르는거죠.
오라클패딩에서는 PKCS7이라는 패딩방식을 사용합니다. PKCS7 패딩방식은 필요한 패딩의 값을 필요한 패딩의 바이트수로 정의합니다.
0x01 |
0x02 |
0x03 |
0x04 |
0x05 |
0x06 |
0x07 |
0x08 |
0x09 |
0x10 |
0x11 |
0x12 |
0x13 |
??? |
??? |
??? |
<표 3>
위와 같은 경우는 3개의 패딩이 필요하니 패딩이 0x03이 들어가
0x01 |
0x02 |
0x03 |
0x04 |
0x05 |
0x06 |
0x07 |
0x08 |
0x09 |
0x10 |
0x11 |
0x12 |
0x13 |
0x03 |
0x03 |
0x03 |
<표 4>
위와 같이 패딩이 들어가게 됩니다. 예시를 보면서 이해해
보도록 합시다.
아래 표는 블록을 4byte로 잘랐을때의 패딩이 어떻게 채워지는지에 대한 표 입니다.
EX |
블록1 |
블록2 |
||||||
EX 1) |
0xAA |
0xBB |
0xCC |
0xDD |
0xEE |
0xFF |
0x02 |
0x02 |
EX 2) |
0XAA |
0xBB |
0x02 |
0x02 |
|
|
|
|
EX 3) |
0xAA |
0x03 |
0x03 |
0x03 |
|
|
|
|
EX 4) |
0xAA |
0xBB |
0xCC |
0xDD |
0x04 |
0x04 |
0x04 |
0x04 |
EX 5) |
0xAA |
0xBB |
0xCC |
0x01 |
|
|
|
|
<표 5>
이제 패딩이 채워지는 규칙에 대해 조금이라도 이해가 되셨을거라고 생각됩니다.
그런데 위의 표를 보면 이상한 부분이 있습니다. EX 4를
보면 4byte의 데이터가 들어가서 데이터가 블록에 딱 맞게 들어가져 있어 뒤에 남은 공간이 없기 때문에
패딩이 필요 없을꺼라고 생각되는데 뒤에 0x04로 블록2를
채우고 있군요.
이런 현상은 왜 일어나게 되는걸까요? 아래와 같은 DATA가
있다고 가정해 봅시다.
DATA1 : 0xAABBCC
DATA2 : 0xAABBCC01
두 데이터를 블록을 4byte로 자르는 블록에 넣어보겠습니다.
DATA |
블록1 |
|||
DATA1 |
0xAA |
0xBB |
0xCC |
0x01 |
DATA2 |
0xAA |
0xBB |
0xCC |
0x01 |
<표 6>
자 성공적으로 넣었습니다. DATA1은 3바이트이기 때문에 패딩 0x01이 들어갔군요!
이제 DATA2를 볼까요? 총 4byte의 데이터를 잘 넣었군요 어? 그런데 이상하네요 DATA1과DATA2가 구분이 가질 않군요!
이제 블록의 byte에 딱 맞는 데이터가 들어갈 경우 다음블록 전체를 패딩으로 채우는 이유를 이해하셨을거라 생각합니다!
Xor
이제 본격적으로 패딩오라클 공격으로 넘어가기전에 마지막으로 알아야 할 것이 있습니다.
바로 Xor 연산입니다. Xor연산은 다음과 같은 성질을 같습니다.
A ⊕ A = 0
A ⊕ 0 = A
A ⊕ B = B ⊕ A
(A ⊕ B) ⊕ C = A ⊕ (B ⊕ C)
∴ A ⊕ B ⊕ B = A ⊕ (B ⊕ B) = A ⊕ 0 = A
⊕은 Xor 을 의미합니다. 위 성질을 보면 Xor 은 교환법칙, 결합법칙등이 성립하는 것을 알 수 있습니다. 위에것을 이해하기 힘드시면 Xor 에 대하여 다시 공부하여 주세요!
패딩오라클공격은 Xor 의 수학적 지식이 필요한 공격입니다.
CBC(Cipher Block Chaining)모드
오라클 패딩공격을 하기 위해서 알아야 할 또 하나의 지식이 나왔습니다. 바로 CBC모드입니다.
블록암호화는 여러 운영모드를 가지고 있는데 패딩오라클에서는 CBC모드를 사용합니다.
CBC모드가 무엇인가 간단히 설명을 해 드리자면, 블록암호가 데이터를 일정 블록으로 나눠 암호화를 시킨다는 것은 이해하셨을 것 입니다.
그런데 각 블록에 들어간 데이터가 같은값을 가지는 경우가 생긴다면 어떻게 될까요?
당연히 같은 데이터가 들어간 블록은 같은 암호화된 결과를 보여주게 될 것 입니다.
CBC모드는 이와 같은 상황을 막기위해 만들어진 모드 입니다.
위 사진은 CBC모드의 암호화과정과 복호화과정을 보여주고
있습니다.
그림에 있는 ⊕ 표시는 수학적 기호로써 Xor을 뜻합니다.
암호화 과정을보면 Plain text를 처음에 IV(Initialization Vector)와 Xor 한 뒤 암호화를 시켜(암호화 알고리즘은 구축하는사람 마음대로지만 보통 3des를 많이 쓰는 것 같더군요) 나온 암호화된 값을 다음 블록의 Xor 피 연산자로 사용하고 있습니다. 따라서 블록에 같은 데이터가 들어있더라고 Plain text와 Xor되는 값이 두번째 블록부터는 IV가 아닌 전 블록의 암호화된 값이기 때문에 암호화된 결과가 같을 염려는 없습니다!
그렇다면 복호화과정에 대하여 알아볼까요?
일단 암호화된 값을 복호화(위 그림에서의 Block Cipher Decryption) 해줍니다! 그 결과물은 무엇일까요?
첫번째 블록을 복호화 한다면 그 값은 (IV) ⊕ (첫번째 블록의 Plaintext) 일 것 입니다.
그럼 위 값에 IV를 Xor 해 준다면?
(IV) ⊕ (IV) ⊕ (첫번째 블록의 Plaintext)
= 0 ⊕ (첫번째 블록의 Plaintext)
= (첫번째 블록의 Plaintext)
즉 Plaintext가 나오게 됩니다. 2번째 블록부터는 IV대신에 전 블록의 암호화된 값을 넣어주면 되겠지요? 왜 IV를 Xor 했는데 Plaintext가 나오는지 이해가 안되셨다면 Xor 연산에대해 좀 더 공부하고 와 주세요!
오라클 패딩 공격
이제 본격적으로 오라클 패딩 공격에 대하여 알아봅시다!
제일 처음에 오라클 패딩이 뭔지에 대한 이야기를 기억하시나요?
저는 패딩오라클을 복호화 시스템에 암호문을 넣었을때에 그에대한
패딩의 올바름 유무를 보여주는 오라클 라고
정의했었습니다. 즉, 바로 이 패딩의 올바름에 대한 서버의
응답을 통해 우리는 암호화된 값을 복호화해 낼 수 있습니다.
일단 우리가 보내는 값에 대한 서버로부터의 응답에 대하여 이해할 필요가 있습니다.
크게 4가지로 나눠보겠습니다.
1. 정상적인 암호화 값을 받았을 경우
2. 비 정상적인 암호화 값(aaaa와 같이 의미없는값)을 받았을 경우
3. 정상적인 암호화 값이지만 패딩이 올바르지 않을 경우
4. 정상적인 암호화 값이지만 복호화 값이 잘못된 경우(예를 들면 복호화 했을 경우 값이 kknock이어야 하는데 ggnock일 경우)
위 4가지 경우에 따라 서버에서의 응답은 다릅니다. 그에 따른 응답은 서버마다 다르므로 각 에러를 구분해 내는 능력이 필요합니다.
자 그렇다면 공격을 시작해 볼까요?
설명을 위해 가상의 서버와 암호문을 생성하겠습니다.
암호문 : 6F7261636C6570647116e1ab1fcf71cd
보통 서버로 보내는 암호문의 경우 IV + 암호화된 값
의 형태로 보내지게 됩니다.
다른 경우도 있으나 exploit 방법 자체는 동일하므로 이 형태로 진행하겠습니다.
암호문이 16byte이므로 8byte 블록 두개로 이루어져 있다고 생각하면
IV : 0x6F 0x72 0x61 0x63
0x6C 0x65 0x70 0x64
Encrypted Plain text : 0x71 0x16 0xe1 0xab 0x1f 0xcf 0x71 0xcd
이렇게 예상할 수 있습니다.
몇 바이트로 나누어져 있는지는 서버에서 CBC모드를 통한 암호화를 할 때 쓰인 암호화 기법에 따라 달라집니다. 이 가상의 서버에서는 3DES암호화 기법을 사용한다는 가정하에 설계하였는데, 3DES는 8byte를 하나의 블록으로 정하기 때문에 위에서 8바이트를 하나의 블록으로 정하였습니다. 실제 서버에서는 블록이 몇 바이트로 이루어져 있는지 모르기 때문에 직접 여러 시도를 하여 알아 내셔야 합니다만 보통은 8byte나 16byte 입니다.
저희가 알고 싶은 값은 Plaintext입니다. 하지만 저희가 알고있는 값은 IV와 암호화된 값(Encrypted Plain text)뿐이군요, 어떻게 Plaintext를 알아낼 수 있을까요?
이제부터는 표를 이용하여 설명해 드리겠습니다.
IV |
0x6F |
0x72 |
0x61 |
0x63 |
0x6C |
0x65 |
0x70 |
0x64 |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
암호화 |
|
||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
위 표는 가상의 서버가 암호화된 값을 어떻게 만들어 냈는지에 대한 방법을 보여주고 있습니다. 물론 실제 서버에서도 위와 같이 동작 하겠지요!
이해가 안되신다면 문서를 다시 읽고 오시는게 좋으실 것 같군요!
이제 Plaintext를 어떻게 얻느냐를 묻겠습니다. 만약 답변으로 그냥 Encrypted Plain text 를 복호화 시켜서 IV ⊕ Plaintext를 얻고 IV를 알고있으니 IV ⊕ IV ⊕ Plaintext 시켜서 얻으면 되잖아요! 위에서 다 알려줬으면서!! ㅎㅎ 라고 말씀하신다면, 착각이 심하시군요! 우리는 현재 암호화 기법이 뭔지 모르고 있습니다! 심지어 안다고 하여도 여기에 쓰이는 암호화 기법들은 대부분 Key를 필요로 합니다. 즉 Key가 없으면 무슨 암호화 기법인지 알아도 복호화가 불가능 합니다! 그게 저희 가상의 서버가 3DES라는 암호화 기법을 사용하는지 알고 있음에도 암호화된 값을 복호화 하지 못하고 있는 이유입니다.
그렇다면 Plaintext를 어떻게 알아낼 수 있을까요? 자, 제가 처음에 오라클 패딩이 뭘 해준다고 말씀드렸죠? 패딩의 올바름의 유무를 알려준다고 말씀드렸습니다! 또한 보낸 암호문에 따른 서버로부터의 응답이 다르다는 것도 알려드렸죠!
이정도 말해드렸으면 눈치채셨을 거라고 생각합니다!
일단 서버로 암호문을 보냈을 때 처리를 어떻게 하는지에 대해 다시한번 생각해봅시다.
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x6F |
0x72 |
0x61 |
0x63 |
0x6C |
0x65 |
0x70 |
0x64 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
위 표는 서버로 암호문을 보냈을 때 서버의 암호문 처리과정을 보여주고 있습니다.
여기서, Plaintext의 결과에 따라 서버에서는 각기
다른 결과를 보여주게 됩니다.
위에서 말했듯이 Plaintext가 올바른 값일 경우, 예상한
값이 아닌 경우, 패딩이 안 맞을
경우 모두 다른 결과를 보여주게 되지요.
자 우리의 가상의 서버에 저 암호문을 그대로 보내 봅시다. 그에 대한 결과로
GOOD
이라는 결과가 나왔네요!(여기서 나온 결과는 제가 만든 가상의 서버의 결과입니다 실제 서버에서는 무슨 결과가 올지 아무도 모릅니다!)
그렇다면 암호문을 조금 바꿔볼까요?
기존 암호문 :
6F7261636C6570647116e1ab1fcf71cd
변경 암호문 : 6A7261636C6570647116e1ab1fcf71cd
자 암호문을 변경하였습니다.
그 결과로 가상의 서버에서의 응답이
BadData
라고 왔네요 왜일까요?? 한번 추측해 봅시다
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x6A |
0x72 |
0x61 |
0x63 |
0x6C |
0x65 |
0x70 |
0x64 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
표를 보시면 알듯이, IV의 0x6F가 0x6A로 바뀜으로써 Plaintext가 새로운 값으로 바뀌어버렸기 때문에 서버에서 저런 응답을 보낸 것으로 추측할 수 있겠군요!
그렇다면 암호문을 아까랑은 또 다르게 바꿔볼까요??
기존 암호문 :
6F7261636C6570647116e1ab1fcf71cd
변경 암호문 : 6F7261636C6570007116e1ab1fcf71cd
이번에는 IV의 제일 마지막 부분을 바꿔보았습니다. 우리의 가상의 서버에 한번 보내볼께요 응답이 어떻게 올까요?
Padding Error
어? 응답이 아까와는 다릅니다. 똑같은 암호문의 IV부분을 바꿨는데 왜 응답이 다를까요? 표를 한번 봅시다!,
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x6F |
0x72 |
0x61 |
0x63 |
0x6C |
0x65 |
0x70 |
0x00 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
아까는 IV의 제일 첫 바이트를 변경하였기 때문에 Plaintext의 첫 바이트가 변경되었지만 이번에는 제일 끝 바이트를 변경해서 Plaintext의 끝 바이트가 변경되었군요!
이게 무슨 차이가 있냐구요? 바로 패딩! 패딩의
차이입니다.
우리는 Plaintext가 뭔지는 몰라도, Encrypted Plain text가 8바이트인걸로 봐서 Plaintext가 적어도 8바이트 데이터는 아니라는걸 알 수 있습니다! 만일 8바이트였다면 뒤에 0x08패딩 8개가 더 들어가 16바이트의 Encrypted Plain text가 만들어 졌겠죠?
그렇다면, Plaintext의 뒷부분은 0x01일지 0x02일지 뭔진 몰라도 패딩으로 채워져 있었을 것 입니다. 그런데 그 패딩이었어야 하는 부분을 저희가 IV값을 바꿔버리면서 다른 값으로 변경시켜버렸기 때문에 패딩이 맞지 않게 되어버려 서버에서 또 다른 결과값을 보내버린 것 입니다! 제가 만든 가상의 서버 같은 경우는 Padding Error 라는 값 이었죠
자 그렇다면 제가 만약 IV의 맨 뒤의 값을 조작하여 Plaintext의 맨 뒤 바이트가 0x01이 되도록 만든다면 서버에서 어떤 결과를 보내올까요? 생각해 보세요 알 수 있습니다.
네 바로 BadData 라는 결과값이 나올 것 입니다! 왜냐구요? 맨 뒤 바이트가 0x01이기 때문에 그걸 패딩으로 인식하고 그 앞의 7바이트를 데이터로 인식해 버렸기 때문이죠!
그러면 Plaintext의 맨 뒤 바이트가 0x01이 되는 값을 어떻게 찾을 수 있냐고요??
뭐겠어요… 부르트포싱이죠…
0x00 ~ 0xFF까지 전부 넣어서 돌려보세요 그러다 보면 BadData라는 결과값이 나올 때가 있을거에요! 그때의 그 값이 그 값입니다. 그럼 저의 가상의 서버에 한번 부르트포싱을 돌려보겠습니다!
암호문 : 6F7261636C6570007116e1ab1fcf71cd
암호문의 빨간부분(IV의 제일 마지막 byte이죠)을 00~FF 까지 하나씩 넣어서 서버에 보낼 것 입니다!
아 결과가 나왔군요!
암호문 : 6F7261636C6570677116e1ab1fcf71cd
Padding Error만 뜨다가 67에서 BadData 라는 결과값이 나왔습니다! 이걸 표로 한번 보게 되면 아래와 같습니다!
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
0x66 |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x6F |
0x72 |
0x61 |
0x63 |
0x6C |
0x65 |
0x70 |
0x67 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
0x01 |
0x67일떄 Plaintext에 패딩값 0x01을 만들어주며 4. 정상적인 암호화 값이지만 복호화 값이 잘못된 경우(예를 들면 복호화 했을 경우 값이 kknock이어야 하는데 ggnock일 경우)의 경우에 해당하는 결과인 BadData를 보여주게 되는거죠!
그러면 여기서 한가지 신기한 사실을 알게 됩니다! 바로 저희가 IV값과 Plaintext의 마지막 바이트를 알게 되었다는 거죠! 그러면 그 둘을 Xor 하게 되면 IV ⊕ Plaintext 의 제일 마지막 바이트가 뭔지 알게 됩니다!
IV ⊕
Plaintext ⊕ IV = Plaintext
IV ⊕
Plaintext ⊕ IV ⊕ IV
= Plaintext ⊕ IV
IV ⊕
Plaintext ⊕ 0 =
Plaintext ⊕ IV
IV ⊕
Plaintext = Plaintext ⊕ IV
따라서 IV ⊕
Plaintext의 제일 마지막 바이트는 0x66이 됩니다!
이해 되셨나요? 안되신다면 Xor 에 대하여
다시 공부를 하셔야 합니다!
위와같이 IV를 이용한 부르트 포스를 이용하면 Encrypted Plain text의 복호화 값인 IV ⊕ Plaintext를 한 바이트씩 얻을 수 있게 됩니다. IV는 아무 바이트로 해도 무관하나 보통은 0000000000000000과 같이 0x00으로 채운 값으로 IV를 변경 후 공격 한답니다. 따라서 지금부터는 IV를 0x0000000000000000 로 바꾸고 설명드리겠습니다!
자 이제 그 다음 바이트를 알아내야겠죠?? 그 다음 바이트를 알아내려면 Plaintext를 어떤형태로 만들어야 할까요?
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
0x02 |
0x02 |
네 맞습니다! 위와 같은 형태로 Plaintext를 만들어내야 서버에서 BadData 를 반환하고, 그떄의 IV값을 이용하여 뒤에서 두번째의 IV ⊕ Plaintext 값을 알아낼 수 있습니다!
그렇다면 위 표와 같이 페딩규칙을 만족시키기 위해 Plaintext의 마지막 바이트의 값이 0x02가 나와야 하는데, 위에서 구한 IV값은 0x01이 나오는군요! 어떻게 해야 할까요?
지금 저희는 IV ⊕ Plaintext의 마지막 바이트 값을 알고 있습니다. 또한 나왔으면 하는Plaintext가 0x02라는것도 알 고 있습니다! 그렇다면 Plaintext의 마지막 바이트를 0x02로 만들기 위한 IV의 값을 알 수 있습니다!
0x66^0x02 = 0x64 (IV ⊕ Plaintext ⊕ Plaintext = IV)
네 나왔습니다 0x64입니다. 즉 IV의 제일 마지막 바이트를 0x64로 놓고 그 다음 바이트의 Plaintext가 0x02가 되게 하는 IV값을 찾으면 된다는 이야기이죠? 그럼 저희가 변경한 암호문을 한번 볼까요?
암호문 : 0000000000000000647116e1ab1fcf71cd
앞에서 말했듯이 IV를 0x0000000000000000으로 변경한 뒤 수정하였습니다
위와 같은 암호문을 서버에 주게되면
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
0x66 |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |
0x64 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
?? |
0x02 |
서버는 위과 같이 처리할 것 입니다. 그럼 아까와 같이 부루트포싱을 날려봅시다!
아 나왔습니다! 0x70에서 BadData가 나왔군요!
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
0x72 |
0x66 |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |
0x00 |
0x70 |
0x64 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
?? |
?? |
?? |
?? |
?? |
?? |
0x02 |
0x02 |
서버에서는 위와 같이 처리 되었겠군요! 또한 이를 통해 IV ⊕ Plaintext의 뒤에서 2번째 바이트 값을 알아냈습니다!
이제 아셨나요?? IV값을 고정시킨 상태로 IV ⊕ Plaintext 값을 구하는게 아니라 뒤에서부터 알맞은 패딩값이 나오도록 계속 바꿔가면서 필요한 패딩을 만들어내고 그를 통해서 IV ⊕ Plaintext 값을 찾아 나아가는 것 입니다
그렇다면 IV ⊕ Plaintext의 그 다음 바이트, 즉 뒤에서 3번째 바이트를 찾기위한 IV값은어떻게 구할까요? 이제 필요한 패딩은 0x03입니다. IV의 뒤 2바이트값은
0x66^0x03
= 0x65
0x72^0x03 = 0x71
라는 식을 통해서 얻을 수 있습니다.
이제 뒤에서 3번째 바이트의 패딩값이 0x03인
경우의 IV값을 찾아내면 뒤에서 3번째의 IV ⊕ Plaintext 값을 구할
수 있을 것 입니다. 따라서 0x0000000000006571 를 IV 값으로 넣어주고 빨간
부분을 0x00 부터 0xFF 까지 부루트포싱 하면서 BadData가 나오는 값을 찾아내는거죠!
이러한 행위를 반복하면 결국 모든 I
Padding Oracle Attack for beginner2 -kknock.pdf
V ⊕ Plaintext 값을 알아낼 수 있습니다!
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
0x04 |
0x19 |
0x0C |
0x0F |
0x0E |
0x72 |
0x72 |
0x66 |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x0C |
0x11 |
0x07 |
0x04 |
0x07 |
0x06 |
0x7A |
0x6E |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
0x08 |
0x08 |
0x08 |
0x08 |
0x08 |
0x08 |
0x08 |
0x08 |
자 처음에 우리가 아는 정보는 Encrypted Plain text와 IV뿐이었지만, 서버의 패딩에 대한 응답을 이용해서 IV ⊕ Plaintext 값을 모두 찾아냈습니다!
그렇다면 Plaintext를 얻어낼 수 있겠죠? 어떻게 얻어내냐구요?? 이 질문 하신분은 반성하셔야 합니다.
IV ⊕ Plaintext에 IV를 Xor 하게 되면
IV ⊕ IV
⊕ Plaintext
0 ⊕ Plaintext
Plaintext
Plaintext만 남게 되는군요! 그렇다면 IV인 0x6F7261636C657064 와 IV ⊕ Plaintext 값인 0x04190F0C0F0E7266를 Xor 시켜보겠습니다.
0x6F7261636C657064^0x04190F0C0F0E7266 = 0x6B6B6E6F636B0202
네, Plaintext는 kknock 이었습니다.
그러면 평문은 얻었는데 서버에 공격을 하려면 조작한 평문값을 서버로 보내야 합니다!그렇다면 조작한 평문값은 어떻게 넘겨줘야 할까요??
여기서도 수학적 계산으로 넘어가게 됩니다.
자, 한번 kknock이라는 글자를 kknock2 라고 바꿔서 서버에 전송하고 싶다고 가정해 봅시다. 조작된 Plaintext는 아래와 같습니다.
0x6B6B6E6F636B3201
Kknock2와 패딩으로 0x01을 넣어준 완벽한 문장이군요. 이 문장을 FPlaintext라고 칭하겠습니다.
우리의 목표는 서버가 암호문을 복호화 했을 때 kknock2가 나오게 하는 것 입니다. 하지만 우리는 Encrypted Plain text를 수정할 수 없습니다. Encrypted Plain text를 만들기 위해서는 IV ⊕ Plaintext를 암호화(가상의 서버에서는 3DES) 해야하는데 서버가 복호화 할 수 있는 암호화를 하기 위해서는 서버가 암호화를 하는데 사용한 Key가 필요하기도 하고, 실제 서버에서는 무슨 암호화 방식을 사용하는지 우리가 알 길이 없기 때문이죠.
따라서 IV ⊕ Plaintext는 고정시키고, IV를 변경함으로써 Plaintext를 변경하도록 해야합니다!
자 일단 우리는 Decrypted Plain text를 복호화 하면 나오는 값이
IV ⊕ Plaintext |
0x04 |
0x19 |
0x0C |
0x0F |
0x0E |
0x72 |
0x72 |
0x66 |
라는 것을 알 고 있습니다.
그리고 여기에 IV를 Xor하여 Plaintext를 얻는다는 사실도 알고있지요 그렇다면 우리가 IV값을 변조하고 그 값을 IV ⊕ Plaintext와 Xor 시켜 원하는 평문(FPlaintext)을 도출하게 할 수 있지 않을까요?,
그렇다면 식을 새워 봅시다.
Plaintext ⊕ IV ⊕ fakeIV = FPlaintext
라는 식이 성립해야 하므로 양 변에 Plaintext ⊕ IV를
Xor 하면
(Plaintext ⊕ IV) ⊕ (Plaintext
⊕ IV)
⊕ fakeIV = FPlaintext ⊕ Plaintext ⊕ IV
0 ⊕ fakeIV = FPlaintext ⊕ Plaintext ⊕ IV
fakeIV = FPlaintext ⊕ Plaintext ⊕ IV
FPlaintext를 만들기 위한 IV값을 구하는 식이 완성되었습니다!
자 우리는 FPlaintext를 가지고 있고, Plaintext도 있으며 IV도 있습니다.
이제 fakeIV를 만들어 봅시다.
0x6B6B6E6F636B3201^0x6B6B6E6F636B0202^0x6F7261636C657064
=0x6F7261636C654067
fakeIV값이 나왔습니다 그렇다면 서버에 한번 보내 볼까요?
암호문 : 6F7261636C6540677116e1ab1fcf71cd
Encrypted Plain text |
0x71 |
0x16 |
0xe1 |
0xab |
0x1f |
0xcf |
0x71 |
0xcd |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
복호화 |
|||||||||
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
||
IV ⊕ Plaintext |
0x04 |
0x19 |
0x0C |
0x0F |
0x0E |
0x72 |
0x72 |
0x66 |
|
|
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
⊕ |
|
IV |
0x6F |
0x72 |
0x61 |
0x63 |
0x6C |
0x65 |
0x40 |
0x67 |
|
|
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
↓ |
|
Plaintext |
0x6B |
0x6B |
0x6E |
0x6F |
0x63 |
0x6B |
0x32 |
0x01 |
Plaintext가 생각했던 kknock2로
나오는군요. 성공입니다.
만일 Encrypted Plain text가 더 길어서 블록이 많아진다고 해도 걱정하지
마세요 그저 첫번째 다음 블록부터는 IV값 대신에 앞에 블록의 Encrypted
Plain text를 넣어주면 되고, 나머지 계산 과정은 같습니다.
이로써 패딩오라클의 원리와 공격 기법까지 모두 설명이 끝났습니다.
초보자분들을 위해 최대한 세세히 적으려고 노력해 봤는데 잘 전해졌는지 모르겠네요.’
수고하셨습니다.
참조
https://blog.skullsecurity.org/2013/padding-oracle-attacks-in-depth
http://laughfool.tistory.com/31
댓글