31 Dec 2014 @ 12:17 AM 

OTP (one time password) challenge gave us a website with username/password/otp as login scheme. A sample login of admin was provided. Since OTP is OTP :P, it won’t work for the second time. We need to understand how exactly OTPs are generated.

Once we create a login in the website, we are provided with an option to download and use OTP generator. Each user gets an option to create a config (token) file, which should be used to feed the OTP generator binary. OTP values contain counters, which will make sure that different OTP gets generated every time.

Reversing OTP binary

Provided binary is ELF x64 C++ binary. Useful symbols were there to help reverse engineer the binary. The config/token files should look like this.

tokenFirst 8 bytes is key-id/random number, second 8 bytes is counter and rest is username. After spending some time with IDA and debugging, we figured the token generating algorithm. It more or less looked like this
1. Username + 7 bytes of key-id is hashed(SHA2) and XORed byte by byte – First 8 bytes of OTP
2. Counter – Next 8 bytes of OTP
3. Counter + 8 bytes of key-id is hashed(SHA2) – last 16 bytes of OTP

Now we know the algorithm, Admin’s OTP and to create the next OTP, we need to know his key-id/token file (8bytes).

Recovering OTP key-id

To attack this scheme, we have to figure out a way to predict the 7 bytes from first part of OTP. An all out brute-force may take ages to complete (even if we precompute values). At this point I consulted dcoder, who pointed me to do meet-in-the-middle.

We are attacking a 7 byte combination. So the idea is to precompute hashes of 3 byte combination, and try to find a match when creating 4 byte hashes. Code should look like this:

uint64 MakeBaseSum()
{
	uint64 base = 0; 
	uint8 data[] ="\x61\x64\x6d\x69\x6e\x00";		//admin\0

	base = SingleHash(0, 0);
	for(int i=0;i<6;i++)
	{
		base ^= SingleHash(data[i], i);
	} 
	return base;
}
void MiTM()
{
	uint64 basesum;
	uint8 target_a[] = "\x9a\xe6\x84\xca\x58\x32\x14\xd3";	//hash
	uint64 p0sum[256];
	uint64 p1sum[256];
	uint64 p2sum[256];
	uint64 p3sum[256];
	uint64 p4sum[256];
	uint64 p5sum[256];
	uint64 p6sum[256];
	uint64 target; 
	memcpy(&target, target_a, sizeof target);

	basesum = MakeBaseSum();
	size_t userLen = 6;		//with '\0'
	for(int i=0;i<256;i++)
	{
		p0sum[i] = SingleHash(i, userLen + 0);
		p1sum[i] = SingleHash(i, userLen + 1);
		p2sum[i] = SingleHash(i, userLen + 2);
		p3sum[i] = SingleHash(i, userLen + 3);
		p4sum[i] = SingleHash(i, userLen + 4);
		p5sum[i] = SingleHash(i, userLen + 5);
		p6sum[i] = SingleHash(i, userLen + 6);
	}
	printf("Tables created.\n");
	unordered_map<uint64, uint32> map;
	map.reserve(1<<24);
	//all 3 byte combination
	for(uint32 i=0; i < (1<<24); i++)
	{
		uint64 temp = basesum ^ p0sum[i&0xFF] ^ p1sum[(i>>8)&0xFF] ^ p2sum[(i>>16)&0xFF];
		map.insert(std::pair<uint64, uint32>(temp, i));
	}
	printf("3 byte done.\n");
	//all 4 byte combination
	for (uint64 i= 0; i < (1LL<<32); i++)
	{
		uint64 temp = p3sum[i&0xFF] ^ p4sum[(i>>8)&0xFF] ^ p5sum[(i>>16)&0xFF] ^ p6sum[(i>>24)&0xFF];
		if(map.count(temp ^ target))
		{
			uint64 full = ((i << 24) + map.at(temp ^ target));
			uint8 bytes[8]; 
			memcpy(bytes, &full, sizeof bytes);
			printf("Found: \n");
			for(size_t j = 0; j < 8; ++j)
				printf("0x%02X,", bytes[j]);
			printf("\n");
			break;
		}
	}					
}

After running this for couple of minutes, we got the 7 bytes of key-id. Missing byte can be brute-forced by comparing with the known OTP value.
Final source code for OTP gen should look like this:
int main( int argc, char *argv[] )
{
	int i;
	sha256_context ctx;
	unsigned char sha256sum[32], basesum[32];
	uint8 data[] ="\x61\x64\x6d\x69\x6e\x00\xC1\x0E\x43\x8C\x99\xBB\x49"; //name+7bytes of key-id
	uint8 base[] ="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; //null byte string to hash first
	sha256_starts( &ctx );
	sha256_update( &ctx, base, 0xd );
	sha256_finish( &ctx, basesum );

	//data is hashed byte by byte and xored
	for(i=0;i<0xd;i++)
	{
		uint8 cont[] ="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
		cont[i] = data[i];
		sha256_starts( &ctx );
		sha256_update( &ctx, cont, 0xd );
		sha256_finish( &ctx, sha256sum );
		XOR(basesum, sha256sum);			//byte array XOR
		//PrintHash(sha256sum);
	} 
	//first 8 bytes of OTP
	for(i=0;i<8;i++)
	{
		printf("%02x", basesum[i] );
	}
	//PrintHash(basesum);
	uint8 count[] = "\x3a\x05\x00\x00\x00\x00\x00\x00\x93\xC1\x0E\x43\x8C\x99\xBB\x49"; //computed key-id
	sha256_starts( &ctx );
	sha256_update( &ctx, count, 0x10 );
	sha256_finish( &ctx, sha256sum );

	//second 8bytes - the counter
	for(i=0;i<8;i++)
	{
		printf("%02x", count[i] );
	}
	//signature
	for(i=0;i<16;i++)
	{
		printf("%02x", sha256sum[i] );
	}
	return 0;
}

Now we can login to the webpage and get our flag 🙂

Posted By: Dan
Last Edit: 02 Jan 2015 @ 07:02 PM

EmailPermalinkComments (0)
Tags
Tags: , , , ,
Categories: CTF
 30 Dec 2014 @ 6:15 PM 

In this challenge we get AES implemented in a hardware and some kind of SPI interface to access the hardware.

Looking at the code, the FLAG is encrypted by random password every time we access it. Now we need to find a way to get the password or some info about the password which can be useful.
This is how a normal operation looks like.

hwaesThe initial getkey command returns a 16*’0′, which says that the key/password initially set was NULL. We can verify that by encrypting something offline and checking the encrypted value from the service. We can also see that once encryption is done, they getkey command returns a different value – b4ef5bcb3e92e21123e951cf6f8f188e

Searching that value in the net promptly puts us in the AES key expansion, and we know that we have a key/info leak.
Now the question is, can we derive the actual key/password or all roundkeys from the final roundkey?

Well we can!. Before searching much, I just wrote some “ghetto” code to reverse the key expansion.

//final round key
uint8_t s[] = "\xb4\xef\x5b\xcb\x3e\x92\xe2\x11\x23\xe9\x51\xcf\x6f\x8f\x18\x8e";
int round = 40;

Print(s);
for(int i=0; i<10;i++)
{
	Reverse(s, op, round);
	round -= 4;
	for(int j = 0;j<16; j++)
	{
		s[j] = op[j];
	}
} 
void Reverse(uint8_t* inp, uint8_t* op, int round)
{
	uint8_t tempa[4];
	uint8_t k;
	tempa[0] = inp[0];
	tempa[1] = inp[1];
	tempa[2] = inp[2];
	tempa[3] = inp[3];

	for(int i=4; i<16;i++)
	{
		op[i] = inp[i] ^ tempa[i%4];
		tempa[i%4] = inp[i];
	}
	tempa[0] = op[12];
	tempa[1] = op[13];
	tempa[2] = op[14];
	tempa[3] = op[15];

	//rotate
	k = tempa[0];
	tempa[0] = tempa[1];
	tempa[1] = tempa[2];
	tempa[2] = tempa[3];
	tempa[3] = k;

	//substitute
	tempa[0] = getSBoxValue(tempa[0]);
	tempa[1] = getSBoxValue(tempa[1]);
	tempa[2] = getSBoxValue(tempa[2]);
	tempa[3] = getSBoxValue(tempa[3]);

	int i = round;
	tempa[0] =  tempa[0] ^ Rcon[i/Nk];

	op[0] = inp[0] ^ tempa[0];
	op[1] = inp[1] ^ tempa[1];
	op[2] = inp[2] ^ tempa[2];
	op[3] = inp[3] ^ tempa[3];
	Print(op);
}

reverse_keReplace with the getkey value after encrypted flag is obtained, and we will have the key used for encrypting that flag. Decrypt with obtained key and we have our flag.

Posted By: Dan
Last Edit: 30 Dec 2014 @ 06:27 PM

EmailPermalinkComments (0)
Tags
Tags: , , ,
Categories: CTF

 Last 50 Posts
Change Theme...
  • Users » 1
  • Posts/Pages » 19
  • Comments » 41
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

About



    No Child Pages.