OZ1NG의 뽀나블(Pwnable)

[Python3] 인코딩 범위 무시하고 순수 바이트 값으로 디코딩 하기 (bytes.decode(errors="surrogateescape")) 본문

Tips

[Python3] 인코딩 범위 무시하고 순수 바이트 값으로 디코딩 하기 (bytes.decode(errors="surrogateescape"))

OZ1NG 2022. 7. 14. 23:02

[*] 발단

친구한테 python3에서는 -c 옵션이 제대로 안먹히는 것 같으니 확인 좀 해달라는 연락을 받았다.

실제 테스트를 해보니 python2.7에서와 완벽히 같은 입력이지만 확실히 결과가 다른 것을 확인 할 수 있었다.

[사진1] - 문제

[*] 원인

python3에서 바이트 표현 값에 0xc2가 있는 것을 보고 눈치챈 사람들도 있겠지만, 이 둘의 결과가 다른 이유는 문자열 인코딩 문제였다.

python3는 디폴트로 utf-8을 사용하고 python2는 디폴트로 ascii를 사용한다.

 

그럼 python3에서 python2와 같은 결과를 내려면 어떻게 해야할까?

 

[*] 변환 방법

위와 같은 일반적인 입력 방법으로는 원하는 모습으로 변환이 되지 않았기 때문에 utf-8을 ascii로 재인코딩 하는 방법을 사용하기로 했다. (이전에 당연히 바이트 타입으로도 출력해봤지만 오히려 '\x' 가 붙어버려 완전히 다른 코드가 되어 버렸다.)

 

여러가지 변환 방법을 사용 해봤지만 시간 단축을 위해 미리 해결 방법을 말하면, python3의 경우 다음과 같은 방식으로 python2와 같은 결과를 낼 수 있다.

[사진2] - 해결 방법

바이트 타입의 값을 ascii로 디코딩 할 때, .decode("ascii", errors="surrogateescape") 와 같이 errors 변수에 "surrogateescape" 값을 주면 된다. (참고로 errors의 디폴트 값은 "strict" 이다.)

errors 옵션은 예외 처리 방식을 설정하는 변수로 6개 정도의 방식이 있다.

[사진3] - errors 값

errors = "surrogateescape"는 디코딩 할 때, 변환할 문자열 타입의 범위를 벗어나는 경우 해당 바이트를 유지한 상태로 변환하는 방식이다.

- 참고 : https://docs.python.org/3/library/codecs.html#error-handlers

 

codecs — Codec registry and base classes — Python 3.10.5 documentation

codecs — Codec registry and base classes Source code: Lib/codecs.py This module defines base classes for standard Python codecs (encoders and decoders) and provides access to the internal Python codec registry, which manages the codec and error handling

docs.python.org

따라서 python3 -c 를 사용하는 경우에는 위와 같은 방법으로 변환하면 원하는대로 변환이 가능하다.

 

[*] 시도 했던 방법들

누군가에겐 시도했던 방법에 대한 결과가 필요할 수 있으니 결과를 정리해둔다...

 

- 바이트 타입으로 출력

oz1ng@LAPTOP-6F0C4A2N:~/tmp$ python3 -c 'a = b"A"*(0x10) + b"\xa0\x91\x04\x08"; print(a)' |xxd
00000000: 6227 4141 4141 4141 4141 4141 4141 4141  b'AAAAAAAAAAAAAA
00000010: 4141 5c78 6130 5c78 3931 5c78 3034 5c78  AA\xa0\x91\x04\x
00000020: 3038 270a                                08'.

- int.to_bytes()

oz1ng@LAPTOP-6F0C4A2N:~/tmp$ python3 -c 'a = b"A"*(0x10) + (0x080491a0).to_bytes(4, byteorder="little"); print(a)' |xxd
00000000: 6227 4141 4141 4141 4141 4141 4141 4141  b'AAAAAAAAAAAAAA
00000010: 4141 5c78 6130 5c78 3931 5c78 3034 5c78  AA\xa0\x91\x04\x
00000020: 3038 270a                                08'.

- struct.pack()

oz1ng@LAPTOP-6F0C4A2N:~/tmp$ python3 -c 'import struct;a = b"A"*(0x10) + struct.pack("<I", 0x080491a0); print(a)' | xxd
00000000: 6227 4141 4141 4141 4141 4141 4141 4141  b'AAAAAAAAAAAAAA
00000010: 4141 5c78 6130 5c78 3931 5c78 3034 5c78  AA\xa0\x91\x04\x
00000020: 3038 270a                                08'.

- pwn.p32()

oz1ng@LAPTOP-6F0C4A2N:~/tmp/$ python3 -c 'import pwn;a = b"A"*(0x10) + pwn.p32(0x080491a0); print(a)' | xxd
00000000: 6227 4141 4141 4141 4141 4141 4141 4141  b'AAAAAAAAAAAAAA
00000010: 4141 5c78 6130 5c78 3931 5c78 3034 5c78  AA\xa0\x91\x04\x
00000020: 3038 270a                                08'.

 

Comments