[Writeup]Nullcon-17 re: EasyPeasy

Vivek Kamisetty
Bug Bounty Hunting
Published in
3 min readSep 29, 2019

--

The challenge involves many recursive functions and it is basically written in C++, this makes the problem a bit complex . From the code, MD5 and SHA initialization are done, so this problem involves hashes.

0x401589:	call   0x401210 <MD5_Init@plt>
0x40158e: lea rax,[rbp-0x110]
0x401595: mov rdi,rax
0x401598: call 0x4012e0 <SHA1_Init@plt>
0x40159d: mov QWORD PTR [rbp-0x1f0],0x0
v40 = __readfsqword(0x28u);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "welcome to easypeasy.", a2);
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
sub_401EFC(&v31);
std::string::string((std::string *)&v24);
MD5_Init(&v34);
SHA1_Init(&v35);
for ( i = 0LL; i <= 6; ++i )
{
sub_40146D(&v31, i);
v4 = (_QWORD *)std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v24);
if ( std::basic_ios<char,std::char_traits<char>>::operator void *((char *)v4 + *(_QWORD *)(*v4 - 24LL)) == 0 )
{
std::operator<<<std::char_traits<char>>(&std::cout, "The flag is ....", v5);
sleep(0x283Au);
a3 = -2;
goto LABEL_24;
}
std::allocator<char>::allocator(&v33);
v6 = std::string::end((std::string *)&v24);
v7 = std::string::begin((std::string *)&v24);
sub_401F7C(&v32, v7, v6, &v33);
std::allocator<char>::~allocator(&v33);
sub_401B36(&v25, &v32);
sub_40204C(&v33, &v31);
LOBYTE(v6) = sub_401B50(&v25, &v33, &v26);
sub_401F16(&v33);
if ( (_BYTE)v6 )
{
v9 = sub_4014D9(i);
v10 = qword_6052A0[i + v9];
if ( v10 == v26 )
{
v11 = std::string::size((std::string *)&v24);
v12 = std::string::c_str((std::string *)&v24);
MD5_Update(&v34, v12, v11);
v13 = std::string::size((std::string *)&v24);
v14 = std::string::c_str((std::string *)&v24);
SHA1_Update(&v35, v14, v13);
v17 = 1;
}
else
{
v15 = std::operator<<<std::char_traits<char>>(&std::cout, "Not good enough.", v10);
std::ostream::operator<<(v15, &std::endl<char,std::char_traits<char>>);
a3 = -3;
v17 = 0;
}
}
else
{
v16 = std::operator<<<std::char_traits<char>>(&std::cout, "This is supposed to be easy.", v8);
std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
a3 = -1;
v17 = 0;
}
sub_401FE6(&v32);
if ( v17 != 1 )
goto LABEL_24;
}
MD5_Final(v36, &v34);
SHA1_Final(v37, &v35);
for ( j = 0LL; j <= 0xF; ++j )
sprintf(&s1[2 * j], "%02x", (unsigned __int8)v36[j]);
for ( k = 0LL; k <= 0x13; ++k )
sprintf(&v39[2 * k], "%02x", (unsigned __int8)v37[k]);
if ( !strcmp(s1, "3d5f443a57358deb84191dec1bfc65fe") )
{
v19 = std::operator<<<std::char_traits<char>>(&std::cout, "That was easy anyways.", v18);
std::ostream::operator<<(v19, &std::endl<char,std::char_traits<char>>);
std::operator<<<std::char_traits<char>>(&std::cout, "Here is your flag: flag{", v20);
for ( l = 0LL; l <= 0x27; ++l )
std::operator<<<std::char_traits<char>>(
&std::cout,
(unsigned int)(char)(v39[l] ^ (unsigned __int64)qword_605340[l]));
v22 = std::operator<<<std::char_traits<char>>(&std::cout, "}", v21);
std::ostream::operator<<(v22, &std::endl<char,std::char_traits<char>>);
}
a3 = 0;
LABEL_24:
std::string::~string((std::string *)&v24);
sub_401F16(&v31);
return a3;

By looking at the above pseudo code we can tell that, there is a loop that iterates seven times, by this we can say that there are seven inputs. The flow of the program would be like-
First, it initials the md5 and sha and then takes the input and wanders into different functions, but in a specific function 0x4016a8, here our input is compared to some characters. if the value inputted is a number then it stores the value.

0x401bc3:	movzx  eax,BYTE PTR [rax]
0x401bc6: mov BYTE PTR [rbp-0x25],al

And the stored value is compared to a specific value

0x401cb7:	cmp    rax,QWORD PTR [rbp-0x8]

if it is a character (+,-,*,/) then this function 0x401ad7 is called, where the character comparison takes places and the cycle goes on.

0x401ad7:	push   rbp
0x401ad8: mov rbp,rsp
0x401adb: mov eax,edi
0x401add: mov BYTE PTR [rbp-0x14],al
0x401ae0: movsx eax,BYTE PTR [rbp-0x14]
0x401ae4: sub eax,0x28
0x401ae7: cdqe
0x401ae9: mov QWORD PTR [rbp-0x8],rax
0x401aed: mov rax,QWORD PTR [rbp-0x8]
0x401af1: cmp rax,0x3

After every line of input the md5 and sha gets updated as,

0x40172e:	call   0x401110 <MD5_Update@plt>
0x401733: lea rax,[rbp-0x210]
0x40173a: mov rdi,rax

0x40180D , here it checks whether, the input count has reached 7 or not. If so, it moves to the other direction of the flow there it finalizes the md5 and sha .

0x401905:	lea    rax,[rbp-0x80]
0x401909: mov esi,0x403770
0x40190e: mov rdi,rax
0x401911: call 0x401280 <strcmp@plt>

Here , it it undergoes string comparison with the string “3d5f443a57358deb84191dec1bfc65fe”
If the comparison is true then the flag is printed.

flag{c0u8a_3dgety7_33hygt_donfos_9uck3d_up_NN}

I hope this helps!

--

--

Vivek Kamisetty
Bug Bounty Hunting

aka Mr_UnKnOwN | CTF player | Reverse Engineer | @teambi0s