Created
November 30, 2012 21:57
-
-
Save kevinoconnor7/4178981 to your computer and use it in GitHub Desktop.
Proxy code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
@file proxy.cpp | |
@brief Top level proxy implementation file | |
*/ | |
#include <unistd.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <netdb.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <sstream> | |
#include <pthread.h> | |
#include <string.h> | |
#include "hex.h" | |
void* client_thread(void* arg); | |
std::string encodePacket(byte* packet, int length); | |
std::string crackUsername(const std::string& plaintext1, const std::string& plaintext2, const std::string& ciphertext1, | |
const std::string& ciphertext2); | |
std::string crackPlaintext(const std::string& plaintext1, const std::string& plaintext2, const std::string& ciphertext1, | |
const std::string& ciphertext2); | |
bool verifyXOR(const std::string& plaintext1, const std::string& plaintext2, | |
const std::string& ciphertext1, const std::string& ciphertext2); | |
std::string XORHexStrings(std::string i1, std::string i2); | |
std::string encodeString(std::string input); | |
std::string decodeString(std::string input); | |
unsigned int stringToHex(std::string input); | |
std::string hexToString(unsigned int input); | |
unsigned short g_bankport; | |
int main(int argc, char* argv[]) | |
{ | |
if(argc != 3) | |
{ | |
printf("Usage: proxy listen-port bank-connect-port\n"); | |
return -1; | |
} | |
unsigned short ourport = atoi(argv[1]); | |
g_bankport = atoi(argv[2]); | |
//socket setup | |
int lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
if(!lsock) | |
{ | |
printf("fail to create socket\n"); | |
return -1; | |
} | |
//listening address | |
sockaddr_in addr_l; | |
memset(&addr_l,0,sizeof(addr_l)); | |
addr_l.sin_family = AF_INET; | |
addr_l.sin_port = htons(ourport); | |
unsigned char* ipaddr = reinterpret_cast<unsigned char*>(&addr_l.sin_addr); | |
ipaddr[0] = 127; | |
ipaddr[1] = 0; | |
ipaddr[2] = 0; | |
ipaddr[3] = 1; | |
if(0 != bind(lsock, reinterpret_cast<sockaddr*>(&addr_l), sizeof(addr_l))) | |
{ | |
printf("failed to bind socket\n"); | |
return -1; | |
} | |
if(0 != listen(lsock, SOMAXCONN)) | |
{ | |
printf("failed to listen on socket\n"); | |
return -1; | |
} | |
//loop forever accepting new connections | |
while(1) | |
{ | |
sockaddr_in unused; | |
socklen_t size = sizeof(unused); | |
int csock = accept(lsock, reinterpret_cast<sockaddr*>(&unused), &size); | |
if(csock < 0) //bad client, skip it | |
continue; | |
pthread_t thread; | |
pthread_create(&thread, NULL, client_thread, (void*)csock); | |
} | |
} | |
void* client_thread(void* arg) | |
{ | |
unsigned int atmTime = time(NULL)+1; | |
int csock = (int)arg; | |
printf("[proxy] client ID #%d connected\n", csock); | |
//create a new socket and connect to the bank | |
int bsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
if(!bsock) | |
{ | |
printf("fail to create socket\n"); | |
return NULL; | |
} | |
sockaddr_in addr_b; | |
addr_b.sin_family = AF_INET; | |
addr_b.sin_port = htons(g_bankport); | |
unsigned char* ipaddr = reinterpret_cast<unsigned char*>(&addr_b.sin_addr); | |
ipaddr[0] = 127; | |
ipaddr[1] = 0; | |
ipaddr[2] = 0; | |
ipaddr[3] = 1; | |
ipaddr[3] = 1; | |
if(0 != connect(bsock, reinterpret_cast<sockaddr*>(&addr_b), sizeof(addr_b))) | |
{ | |
printf("fail to connect to bank\n"); | |
return NULL; | |
} | |
unsigned int bankTime = time(NULL); | |
//input loop | |
int length; | |
char packet[1024]; | |
std::string atmNonce, bankNonce; | |
srand(time(NULL) + 1); | |
std::vector<unsigned int> atmNonces, bankNonces; | |
for(unsigned int i = 0; i < 500; ++i) | |
{ | |
atmNonces.push_back(rand()); | |
} | |
srand(time(NULL)); | |
for(unsigned int i = 0; i < 500; ++i) | |
{ | |
bankNonces.push_back(rand()); | |
} | |
//Setup two variables for breaking plaintext | |
std::string plaintext1 = encodeString("login alice 794582936 7945829364991124672345 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); | |
std::string ciphertext1 = "A392119691E370432D8446750E631A2840D7822565BF35EBE731FDC5C82625486F551A8BDC818FE1773487F72AEFC92C4962170CA2FE4638AC851025AB4D6FC464D6C4D8668B1090485A1C63D25ED49A00BCF93D633EE38CFBACA2DE22811AEDC287573AF6C8D2195C690E94DFAD1AC6D4FB6CCA0B0DE382D1306D9655A8754F917A1C2A80C3A753328E0AC26AA1808E32FEAA064C1D4DC8F60ACD1B1898B4A3"; | |
unsigned int loopCount = 0; | |
while(csock) | |
{ | |
//atmTime = time(NULL)+1; | |
//bankTime = time(NULL); | |
std::string encodedPacket; | |
std::string plaintext2 = ""; | |
//read the packet from the ATM | |
if(sizeof(int) != recv(csock, &length, sizeof(int), 0)) | |
break; | |
if(length >= 1024) | |
{ | |
printf("packet too long\n"); | |
break; | |
} | |
if(length != recv(csock, packet, length, 0)) | |
{ | |
printf("[proxy] fail to read packet\n"); | |
break; | |
} | |
//TODO: tamper with packet going from ATM to bank | |
encodedPacket = encodePacket((byte*)packet,160); | |
printf("[aTb] %s\n", encodedPacket.c_str()); | |
if(encodedPacket.substr(0,12) == "A392119691E3" || | |
encodedPacket.substr(0,18) == "BB8F17918CA5745D64" || | |
encodedPacket.substr(0,14) == "A39211908AB731") | |
{ | |
std::string username = crackUsername(plaintext1,plaintext2,ciphertext1,encodedPacket); | |
if(username.size() > 0) | |
{ | |
printf("[cracked username] %s\n", username.c_str()); | |
} | |
} | |
plaintext2 = crackPlaintext(plaintext1,plaintext2,ciphertext1,encodedPacket); | |
if(plaintext2.size() > 0) | |
{ | |
printf("[aTb plaintext] %s\n", decodeString(plaintext2).c_str()); | |
} | |
//Time to guess the ATM's nonce | |
printf("[atm nonce] %u\n", atmNonces[loopCount]); | |
//forward packet to bank | |
if(sizeof(int) != send(bsock, &length, sizeof(int), 0)) | |
{ | |
printf("[proxy] fail to send packet length 2\n"); | |
break; | |
} | |
if(length != send(bsock, (void*)packet, length, 0)) | |
{ | |
printf("[proxy] fail to send packet\n"); | |
break; | |
} | |
// atmTime = time(NULL)+1; | |
// bankTime = time(NULL); | |
if(sizeof(int) != recv(csock, &length, sizeof(int), 0)) | |
break; | |
if(length >= 1024) | |
{ | |
printf("packet too long\n"); | |
break; | |
} | |
if(length != recv(csock, packet, length, 0)) | |
{ | |
printf("[proxy] fail to read packet\n"); | |
break; | |
} | |
//TODO: tamper with packet going from ATM to bank | |
encodedPacket = encodePacket((byte*)packet,32); | |
printf("[aTb hash] %s\n", encodedPacket.c_str()); | |
//forward packet to bank | |
if(sizeof(int) != send(bsock, &length, sizeof(int), 0)) | |
{ | |
printf("[proxy] fail to send packet length 2\n"); | |
break; | |
} | |
if(length != send(bsock, (void*)packet, length, 0)) | |
{ | |
printf("[proxy] fail to send packet\n"); | |
break; | |
} | |
// atmTime = time(NULL)+1; | |
// bankTime = time(NULL); | |
//get response packet from bank | |
if(sizeof(int) != recv(bsock, &length, sizeof(int), 0)) | |
{ | |
printf("[proxy] fail to read packet length 3\n"); | |
break; | |
} | |
if(length >= 1024) | |
{ | |
printf("packet too long\n"); | |
break; | |
} | |
if(length != recv(bsock, packet, length, 0)) | |
{ | |
printf("[proxy] fail to read packet\n"); | |
break; | |
} | |
//Guess the bank's nonce | |
printf("[bank nonce] %u\n", bankNonces[loopCount]); | |
//TODO: tamper with packet going from bank to ATM | |
encodedPacket = encodePacket((byte*)packet, 160); | |
printf("[bTa] %s\n", encodedPacket.c_str()); | |
plaintext2 = ""; | |
plaintext2 = crackPlaintext(plaintext1,plaintext2,ciphertext1,encodedPacket); | |
if(plaintext2.size() > 0) | |
{ | |
printf("[bTa plaintext] %s\n", decodeString(plaintext2).c_str()); | |
} | |
//forward packet to ATM | |
if(sizeof(int) != send(csock, &length, sizeof(int), 0)) | |
{ | |
printf("[proxy] fail to send packet length 1\n"); | |
break; | |
} | |
if(length != send(csock, (void*)packet, length, 0)) | |
{ | |
printf("[proxy] fail to send packet\n"); | |
break; | |
} | |
// atmTime = time(NULL)+1; | |
// bankTime = time(NULL); | |
if(sizeof(int) != recv(bsock, &length, sizeof(int), 0)) | |
{ | |
printf("[proxy] fail to read packet length 3\n"); | |
break; | |
} | |
if(length >= 1024) | |
{ | |
printf("packet too long\n"); | |
break; | |
} | |
if(length != recv(bsock, packet, length, 0)) | |
{ | |
printf("[proxy] fail to read packet\n"); | |
break; | |
} | |
//TODO: tamper with packet going from bank to ATM | |
encodedPacket = encodePacket((byte*)packet,32); | |
printf("[bTa hash] %s\n", encodedPacket.c_str()); | |
//forward packet to ATM | |
if(sizeof(int) != send(csock, &length, sizeof(int), 0)) | |
{ | |
printf("[proxy] fail to send packet length 1\n"); | |
break; | |
} | |
if(length != send(csock, (void*)packet, length, 0)) | |
{ | |
printf("[proxy] fail to send packet\n"); | |
break; | |
} | |
printf("---------------------------\n"); | |
++loopCount; | |
if(loopCount == bankNonces.size() || loopCount == atmNonces.size()) | |
{ | |
break; | |
} | |
} | |
printf("[proxy] client ID #%d disconnected\n", csock); | |
close(bsock); | |
close(csock); | |
return NULL; | |
} | |
std::string encodePacket(byte* packet, int length) | |
{ | |
//return std::string(""); | |
std::string encodedPacket; | |
CryptoPP::StringSource(packet, length, true, | |
new CryptoPP::HexEncoder( | |
new CryptoPP::StringSink(encodedPacket) | |
) // HexEncoder | |
); | |
return encodedPacket; | |
} | |
std::string encodeString(std::string input) | |
{ | |
//return std::string(""); | |
std::string output; | |
CryptoPP::StringSource(input, true, | |
new CryptoPP::HexEncoder( | |
new CryptoPP::StringSink(output) | |
) // HexEncoder | |
); | |
return output; | |
} | |
std::string decodeString(std::string input) | |
{ | |
//return std::string(""); | |
std::string output; | |
CryptoPP::StringSource(input, true, | |
new CryptoPP::HexDecoder( | |
new CryptoPP::StringSink(output) | |
) // HexEncoder | |
); | |
return output; | |
} | |
unsigned int stringToHex(std::string input) | |
{ | |
unsigned int x; | |
std::stringstream ss; | |
ss << std::hex << input; | |
ss >> x; | |
return x; | |
} | |
std::string hexToString(unsigned int input) | |
{ | |
std::stringstream stream; | |
stream << std::hex << input; | |
std::string result( stream.str() ); | |
return result; | |
} | |
std::string XORHexStrings(std::string i1, std::string i2) | |
{ | |
unsigned int e1 = stringToHex(i1); | |
unsigned int e2 = stringToHex(i2); | |
return hexToString(e1^e2); | |
} | |
bool verifyXOR(const std::string& plaintext1, const std::string& plaintext2, | |
const std::string& ciphertext1, const std::string& ciphertext2) | |
{ | |
if(plaintext1.size() < 16 || plaintext2.size() < 16 || ciphertext1.size() < 16 || ciphertext2.size() < 16) | |
{ | |
return false; | |
} | |
for(unsigned int i = 0; i < 16; ++i) | |
{ | |
std::string plainXOR, cipherXOR; | |
cipherXOR = XORHexStrings(ciphertext1.substr(i*2,2), ciphertext2.substr(i*2,2)); | |
plainXOR = XORHexStrings(plaintext1.substr(i*2,2), plaintext2.substr(i*2,2)); | |
if(plainXOR != cipherXOR) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
std::string crackPlaintext(const std::string& plaintext1, const std::string& plaintext2, const std::string& ciphertext1, | |
const std::string& ciphertext2) | |
{ | |
std::string result(""); | |
if(plaintext1.size() < 16 || ciphertext1.size() < 16 || ciphertext2.size() < 16) | |
{ | |
return ""; | |
} | |
if(plaintext2.size() >= 16) | |
{ | |
return plaintext2; | |
} | |
for(unsigned int i = 0; i < 16; ++i) | |
{ | |
std::string plainXOR, cipherXOR, discoverdPlain; | |
cipherXOR = XORHexStrings(ciphertext1.substr(i*2,2), ciphertext2.substr(i*2,2)); | |
if(plaintext2.size() >= i*2+2) | |
{ | |
discoverdPlain = plaintext2.substr(i*2,2); | |
} else { | |
discoverdPlain = XORHexStrings(plaintext1.substr(i*2,2), cipherXOR); | |
} | |
plainXOR = XORHexStrings(plaintext1.substr(i*2,2), discoverdPlain); | |
if(plainXOR != cipherXOR) | |
{ | |
return ""; | |
} | |
result += discoverdPlain; | |
} | |
return result; | |
} | |
std::string crackUsername(const std::string& plaintext1, const std::string& plaintext2, const std::string& ciphertext1, | |
const std::string& ciphertext2) | |
{ | |
if(!(ciphertext2.substr(0,12) == "A392119691E3" || | |
ciphertext2.substr(0,18) == "BB8F17918CA5745D64" || | |
ciphertext2.substr(0,14) == "A39211908AB731")) | |
{ | |
return ""; | |
} | |
std::string crackedPlain = crackPlaintext(plaintext1,plaintext2,ciphertext1,ciphertext2); | |
if(crackedPlain.size() != 32) | |
{ | |
return ""; | |
} | |
if(ciphertext2.substr(0,12) == "A392119691E3") | |
{ | |
crackedPlain = decodeString(crackedPlain.substr(12)); | |
} | |
else if(ciphertext2.substr(0,18) == "BB8F17918CA5745D64") | |
{ | |
crackedPlain = decodeString(crackedPlain.substr(18)); | |
unsigned int i = 0; | |
for(; i < crackedPlain.size(); ++i) | |
{ | |
if(crackedPlain[i] == ' ') | |
{ | |
break; | |
} | |
} | |
crackedPlain = crackedPlain.substr(i+1); | |
} | |
else if(ciphertext2.substr(0,14) == "A39211908AB731") | |
{ | |
crackedPlain = decodeString(crackedPlain.substr(14)); | |
} else { | |
return ""; | |
} | |
unsigned int i = 0; | |
for(; i < crackedPlain.size(); ++i) | |
{ | |
if(crackedPlain[i] == ' ') | |
{ | |
i -= 1; | |
break; | |
} | |
} | |
return crackedPlain.substr(0,i+1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment