
Hackquest 2018. Results & Writeups. Day 1-3
Seven days of the annual hackquest - seven tasks that need to be solved in order to get free entrance tickets to Zeronights. In this article, we suggest that you familiarize yourself with the decisions of some of them, as well as find out the names of the winners. For convenience, we will split the publication into two parts.


For help in preparing this assignment, thank REhints .
"The challenge is as easy as the following: download the binary (pass: infected), reverse engineer it and recover two secrets that the binary will accept. Each secret is an ASCII string. You would need to solve first stage first before attacking the second. Good luck and may Galois be with you! ”
To get the flag, you need to enter two secret values sequentially

: We get into the function along the lines of the displayed messages:
To reach “Stage # 2 unlocked!” , You must pass a function test
Under the debugger, you can make sure that it

And
data is presented in the form of 128-bit numbers, because it turned out to be more convenient during further analysis.
For each pair of 128-bit numbers addressed by the second argument, the loop runs through 128 bits of the input string (the first argument) and, if the bit is 0, it resets the corresponding bits (in fact, it performs the AND operation). For the second number from a pair of bits, it is also reset if the next bit in the sequence is 0.
On python, it looks something like this:
Каждую получившуюся пару функция сворачивает в один бит, выполняя XOR между всеми битами. Полученные 128 бит и формируют число, сравниваемое с заданным.
Для нахождения решения воспользуемся решателем z3.
Преобразовав найденные биты в строку, получаем первый ключ: ItWasJustAWarmUp.
Введённая строка представляется в виде пяти 64-битных чисел, результат преобразования которых сравнивается с массивом.

Прогулявшись под отладчиком, видим такую последовательность вызова функций:

Пока видно, что третий аргумент передаётся по цепочке вызовов, иногда складываясь по модулю 2 с единицей, иногда с очередным битом введённой строки (хотя некоторые биты, например, первый, пропускаются).
Посмотрим, где происходит модификация преобразуемого числа, поставив точку останова на запись в соответствующие байты памяти:

Т.е. бит числа заменяется на значение третьего аргумента, его предыдущее значение передаётся дальше (иногда инвертированное). Можно предположить, что происходит следующее преобразование:
To determine the unknown constants, we get the converted values for zero and numbers with one bit set. To do this, define a counter
The resulting values are:

From:
So in Python, the conversion function looks like this:
And the reverse to him:
Applying the conversion to numbers from the array

For help in preparing the assignment, thank Vlad Roskov from @leetmore .

When clicking on the link, we see the following:

We are asked to enter a certain promotional code. Let's see which files load when the page opens.

The
В результате получаем необходимое число, первую часть нашего промокода: 234082018.

Первую часть проверки мы прошли. Перейдём ко второй:

Видно, что возвращенное из функции
Погуглив, узнаём, что мы столкнулись с эмулятором, написанным на Javascript.
В переменной
Перейдём в blackbox_check. Здесь мы видим какую-то хэш-функцию. После нескольких неудачных попыток найти возможности для быстрого брута или другие уязвимости в алгоритме, замечаем, что буфер, в который копируется часть строки после тире, ограничен всего 256 символами.
Проверяем с помощью checksec и видим, что стэк является исполняемым:
Примечание — на скриншоте и в тексте автора врайтапа есть расхождения насчет того, является ли стек исполняемым. В действительности, код исполнить можно.

Учитывая то, что мы уже знаем адрес в стэке, куда запишется наша строка, дело за малым — записать в нужный адрес байты из строки «e1ee757bc7fd00d5».

Через консоль отправляем получившуюся строку и забираем флаг:
Искомая строка: Bl4CK_0r_wYT3_It5_z3r0Ni6hT

Hey, this group still use an old-style messaging system. We assume members are in their thirties and such is their crypto. Try finding flaws there. Their messaging system is at 51.15.79.170 And you know what? We could intercept a piece of their authentication request.
You can make use of it ytW81KkHaGOnaqiG7Gr4AA==:XXnpfKJoUCufBm2ztTXGCN6wgqLeScU8+XlL7Co5oHg=
1. I have received 403 error after post “ytW81KkHaGOnaqiG7Gr4AA ==: XXnpfKJoUCufBm2ztTXGCN6wgqLeScU8 + XlL7Co5oHg =” string. If change some letters - still receive 403 error, it change letter count - receive 500 error. After some investigations I realized that first part sould be 16 bytes long in base64-decoded form, and second part must contain some blocks 16 bytes long each. Seems first part is IV and second part - encoded data in CBC mode, and according to task name “pad” it is padding oracle attack.
2. Lets change last byte in base64 decoded form of second part (01.php script, total 256 possibilities). For one case instead of “403” error I have received message “Sir, your message is incorrect or it is not 48 bytes long”

Content

Day1. Galois
For help in preparing this assignment, thank REhints .
"The challenge is as easy as the following: download the binary (pass: infected), reverse engineer it and recover two secrets that the binary will accept. Each secret is an ASCII string. You would need to solve first stage first before attacking the second. Good luck and may Galois be with you! ”
To get the flag, you need to enter two secret values sequentially

: We get into the function along the lines of the displayed messages:
DialogFunc
BOOL __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
HWND v5; // eax
HWND v6; // eax
HWND v7; // eax
HWND v8; // eax
HWND v9; // eax
HWND v10; // eax
CHAR v11; // [esp+0h] [ebp-5Ch]
CHAR String[20]; // [esp+2Ch] [ebp-30h]
struct tagRECT Rect; // [esp+40h] [ebp-1Ch]
UINT v14; // [esp+50h] [ebp-Ch]
HDC hdc; // [esp+54h] [ebp-8h]
unsigned int i; // [esp+58h] [ebp-4h]
v14 = a2;
if ( a2 == 20 )
{
hdc = (HDC)a3;
GetClientRect(hWnd, &Rect);
SetMapMode(hdc, 8);
SetWindowExtEx(hdc, 100, 100, 0);
SetViewportExtEx(hdc, Rect.right, Rect.bottom, 0);
FillRect(hdc, &Rect, hbr);
return 1;
}
if ( v14 == 272 )
return 1;
if ( v14 != 273 || HIWORD(a3) )
return 0;
if ( (unsigned __int16)a3 == 1 )
{
EndDialog(hWnd, (unsigned __int16)a3);
return 1;
}
if ( (unsigned __int16)a3 != 1002 )
{
if ( (unsigned __int16)a3 == 1004 )
{
sub_42E7C0(&v11, 0, 42);
if ( GetDlgItemTextA(hWnd, 1005, &v11, 42) <= 0x27 && (unsigned __int8)sub_42CEF0(&v11) )
{
v9 = GetDlgItem(hWnd, 1004);
EnableWindow(v9, 0);
v10 = GetDlgItem(hWnd, 1005);
EnableWindow(v10, 0);
MessageBoxW(hWnd, L"Good job!", L"Success", 0);
return 1;
}
MessageBoxW(hWnd, L"Keep trying!", L"Rejected", 0);
}
return 1;
}
if ( GetDlgItemTextA(hWnd, 1001, String, 18) != 16 || !(unsigned __int8)sub_4014E0(String) )
{
MessageBoxW(hWnd, L"Keep trying!", L"Rejected", 0);
return 1;
}
v5 = GetDlgItem(hWnd, 1002);
EnableWindow(v5, 0);
v6 = GetDlgItem(hWnd, 1001);
EnableWindow(v6, 0);
v7 = GetDlgItem(hWnd, 1005);
EnableWindow(v7, 1);
v8 = GetDlgItem(hWnd, 1004);
EnableWindow(v8, 1);
for ( i = 0; i < 0x28; ++i )
*((_BYTE *)&dword_442780 + i) ^= String[(signed int)i % 16];
MessageBoxW(hWnd, L"Stage #2 unlocked!", L"Accepted", 0);
return 1;
}
To reach “Stage # 2 unlocked!” , You must pass a function test
sub_4014E0
(line length - 16 characters). And before “Good job!” - another check in sub_42CEF0
(the length of the second line is less than 40 characters).Stage # 1
char __cdecl sub_4014E0(char *a1)
{
int v1; // ecx
unsigned __int128 var_1018[256]; // [esp+0h] [ebp-1018h]
unsigned __int128 var_18; // [esp+1000h] [ebp-18h]
int v5; // [esp+1010h] [ebp-8h]
char v6; // [esp+1017h] [ebp-1h]
sub_438740(0x1018u, v1);
sub_43A460(var_1018, xmmword_4427A8, 0x1000u);
sub_4010E0(a1, var_1018);
sub_401250(var_1018, &var_18);
v5 = sub_438845(var_18, &xmmword_442770, 0x10u);
if ( !v5 )
v6 = 1;
return v6;
}
Under the debugger, you can make sure that it
sub_43A460
copies the data from: 
And
sub_438845
compares the value with: The .data:00442770 xmmword_442770 9698CA91EE29902C60D377C981589205h
data is presented in the form of 128-bit numbers, because it turned out to be more convenient during further analysis.
Sub_4010E0 function
void __cdecl sub_4010E0(unsigned __int128 *a1, unsigned __int128 *a2)
{
signed int i; // [esp+4h] [ebp-8h]
signed int j; // [esp+8h] [ebp-4h]
for ( i = 0; i < 128; ++i )
{
for ( j = 0; j < 128; ++j )
{
if ( (*((_DWORD *)a1 + (j >> 5)) >> (j & 0x1F)) & 1 )
{
if ( !((*((_DWORD *)a1 + ((j + 1) % 128 >> 5)) >> ((j + 1) % 128 & 0x1F)) & 1) )
*((_DWORD *)&a2[2 * i] + ((j + 128) >> 5)) &= ~(1 << ((j + -128) & 0x1F));
}
else
{
*((_DWORD *)&a2[2 * i] + (j >> 5)) &= ~(1 << (j & 0x1F));
*((_DWORD *)&a2[2 * i] + ((j + 128) >> 5)) &= ~(1 << ((j + -128) & 0x1F));
}
}
}
}
The same function in a somewhat simplified form
void __cdecl sub_4010E0(unsigned __int128 *a1, unsigned __int128 *a2)
{
signed int i; // [esp+4h] [ebp-8h]
signed int j; // [esp+8h] [ebp-4h]
signed int j_1;
for ( i = 0; i < 128; ++i )
{
for ( j = 0; j < 128; ++j )
{
j_1 = (j + 1) % 128;
if ( (*((_DWORD *)a1 + j / 32) >> (j % 32)) & 1 )
{
if ( !((*((_DWORD *)a1 + j_1 / 32) >> (j_1 % 32)) & 1) )
*((_DWORD *)&a2[2 * i] + 4 + j / 32) &= ~(1 << (j % 32));
}
else
{
*((_DWORD *)&a2[2 * i] + j / 32) &= ~(1 << (j % 32));
*((_DWORD *)&a2[2 * i] + 4 + j / 32) &= ~(1 << (j % 32));
}
}
}
}
For each pair of 128-bit numbers addressed by the second argument, the loop runs through 128 bits of the input string (the first argument) and, if the bit is 0, it resets the corresponding bits (in fact, it performs the AND operation). For the second number from a pair of bits, it is also reset if the next bit in the sequence is 0.
On python, it looks something like this:
def sub_4010E0(a1, a2):
for i in range(128):
a2[2 * i] &= a1
a2[2 * i + 1] &= a1 & (a1 >> 1)
Function sub_401250
void __cdecl sub_401250(unsigned __int128 *a1, unsigned __int128 *a2)
{
signed int k; // [esp+4h] [ebp-Ch]
signed int j; // [esp+8h] [ebp-8h]
signed int i; // [esp+Ch] [ebp-4h]
*(_QWORD *)a2 = 0i64;
*((_QWORD *)a2 + 1) = 0i64;
for ( i = 0; i < 128; ++i )
{
for ( j = 0; j < 4; ++j )
*((_DWORD *)&a1[2 * i] + j) ^= *((_DWORD *)&a1[2 * i + 1] + j);
for ( k = 0; k < 2; ++k )
*((_DWORD *)&a1[2 * i] + k) ^= *((_DWORD *)&a1[2 * i] + k + 2);
LODWORD(a1[2 * i]) ^= DWORD1(a1[2 * i]);
LODWORD(a1[2 * i]) ^= LODWORD(a1[2 * i]) >> 16;
LODWORD(a1[2 * i]) ^= LODWORD(a1[2 * i]) >> 8;
LODWORD(a1[2 * i]) ^= LODWORD(a1[2 * i]) >> 4;
LODWORD(a1[2 * i]) ^= LODWORD(a1[2 * i]) >> 2;
LODWORD(a1[2 * i]) ^= LODWORD(a1[2 * i]) >> 1;
if ( a1[2 * i] & 1 )
*((_DWORD *)a2 + (i >> 5)) |= 1 << (i & 0x1F);
}
}
Каждую получившуюся пару функция сворачивает в один бит, выполняя XOR между всеми битами. Полученные 128 бит и формируют число, сравниваемое с заданным.
Для нахождения решения воспользуемся решателем z3.
Несложными заменами в блокноте превращаем массив xmmword_4427A8 в систему (нули и единицы в левой части - это число xmmword_442770)
from z3 import *
init('../')
def xor_bits(x):
x = Extract(63, 0, x) ^ Extract(127, 64 ,x)
x = Extract(31, 0, x) ^ Extract(63, 32, x)
x = Extract(15, 0, x) ^ Extract(31, 16, x)
x = Extract(7, 0, x) ^ Extract(15, 8, x)
x = Extract(3, 0, x) ^ Extract(7, 4, x)
x = Extract(1, 0, x) ^ Extract(3, 2, x)
return Extract(0, 0, x) ^ Extract(1, 1, x)
x = BitVec('x', 128)
s = Solver()
# 0x9698CA91EE29902C60D377C981589205
s.add(1 == xor_bits(BitVecVal(0x23B617F87A29, 128) & x) ^ xor_bits(BitVecVal(0x19203F83800, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1091, 128) & x) ^ xor_bits(BitVecVal(0x0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xC1C283565E, 128) & x) ^ xor_bits(BitVecVal(0xC001020E, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x2C1A78A0, 128) & x) ^ xor_bits(BitVecVal(0x4083800, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1B, 128) & x) ^ xor_bits(BitVecVal(0x1, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x2BD, 128) & x) ^ xor_bits(BitVecVal(0x1C, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x6434999B6302B0DBCA8123800BE, 128) & x) ^ xor_bits(BitVecVal(0x10088921001049C000018001E, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x613, 128) & x) ^ xor_bits(BitVecVal(0x1, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x16129091D4A1C4, 128) & x) ^ xor_bits(BitVecVal(0x2000000C000C0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x91, 128) & x) ^ xor_bits(BitVecVal(0x0, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x76749651E3D6AD117C5AF, 128) & x) ^ xor_bits(BitVecVal(0x12300200E1C204003C087, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x42FED750966FE5, 128) & x) ^ xor_bits(BitVecVal(0x7E43000227E0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x19D096E5D570B, 128) & x) ^ xor_bits(BitVecVal(0xC00260C0301, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x2BD67AA37EC064C89AF12D97, 128) & x) ^ xor_bits(BitVecVal(0x1C238013E40204008700483, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x3372FB29B7426152C739DB374312B, 128) & x) ^ xor_bits(BitVecVal(0x1307900930020004318C91301001, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x56D0FAF2FF, 128) & x) ^ xor_bits(BitVecVal(0x24078707F, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x5AAEFBCEFB063DB41635, 128) & x) ^ xor_bits(BitVecVal(0x80679C679021C900210, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x6388B6598BDD29D10862B2D9DC0B95, 128) & x) ^ xor_bits(BitVecVal(0x180120881CC00C000201048CC0180, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1BB63AF4CE98D27, 128) & x) ^ xor_bits(BitVecVal(0x19218704608403, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x5D4D30E2F6807C1D, 128) & x) ^ xor_bits(BitVecVal(0xC04106072003C0C, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x71AE40387FEFC85, 128) & x) ^ xor_bits(BitVecVal(0x108600183FE7C00, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1E320DC34999A60A93AE, 128) & x) ^ xor_bits(BitVecVal(0x61004C1008882000186, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x2F52, 128) & x) ^ xor_bits(BitVecVal(0x700, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xB9D4158F29C2CAEC9, 128) & x) ^ xor_bits(BitVecVal(0x18C0008700C040640, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x6347962CDB1BC, 128) & x) ^ xor_bits(BitVecVal(0x10382044909C, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xA2FF40, 128) & x) ^ xor_bits(BitVecVal(0x7F00, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x6, 128) & x) ^ xor_bits(BitVecVal(0x0, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x21FFD0D835722CFF409DD38B2E11, 128) & x) ^ xor_bits(BitVecVal(0xFFC0481030047F000CC1810600, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x466DC3, 128) & x) ^ xor_bits(BitVecVal(0x224C1, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x45D44FF2, 128) & x) ^ xor_bits(BitVecVal(0xC007F0, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x13ACE, 128) & x) ^ xor_bits(BitVecVal(0x1846, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x3FB1EAE727AD49CCA3, 128) & x) ^ xor_bits(BitVecVal(0xF90E063038400C401, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xB35AD528D, 128) & x) ^ xor_bits(BitVecVal(0x110840004, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x135AB5C363654340F464007DC58, 128) & x) ^ xor_bits(BitVecVal(0x10810C1212001007020003CC08, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x405E95920EEE57BE0E0A51677D05, 128) & x) ^ xor_bits(BitVecVal(0xE00800666039E060000233C00, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xB4DEE4643A9F3218C6, 128) & x) ^ xor_bits(BitVecVal(0x104E6020180F100842, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x678D930A747F3D1CCCC727B, 128) & x) ^ xor_bits(BitVecVal(0x3848100303F1C0C4443039, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x2DB2654501D47A394DC9A, 128) & x) ^ xor_bits(BitVecVal(0x490200000C0381804C08, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xE86CDA733EA, 128) & x) ^ xor_bits(BitVecVal(0x202448311E0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x13124A8FCA2ADE475, 128) & x) ^ xor_bits(BitVecVal(0x1000007C0004E030, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x44F8BC5F2E3551A9F94F8B2221EF6F3, 128) & x) ^ xor_bits(BitVecVal(0x781C0F06100080F807810000E7271, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x27274, 128) & x) ^ xor_bits(BitVecVal(0x3030, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x3558FA658C2, 128) & x) ^ xor_bits(BitVecVal(0x87820840, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1C4CDE17C7DE9258195055E9265D221, 128) & x) ^ xor_bits(BitVecVal(0x4044E03C3CE0008080000E0020C000, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x760DB29068A310965, 128) & x) ^ xor_bits(BitVecVal(0x12049000200100020, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x5F6FB1F353B814B83DFAB039F, 128) & x) ^ xor_bits(BitVecVal(0xF2790F1019800181CF81018F, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xBC0919D732BD93E1E7977D3B7B, 128) & x) ^ xor_bits(BitVecVal(0x1C0008C3101C81E0E3833C1939, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x5D31D733E3387235ECC, 128) & x) ^ xor_bits(BitVecVal(0xC10C311E1183010E44, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x2F6A4694FA4052C180C72D95FF, 128) & x) ^ xor_bits(BitVecVal(0x72002007800004080430480FF, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xFFBC69460FD68BE9D350, 128) & x) ^ xor_bits(BitVecVal(0x3F9C200207C201E0C100, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x14BDFBEFE7, 128) & x) ^ xor_bits(BitVecVal(0x1CF9E7E3, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x253F95DEE35BCE76A0EA615EB11D698, 128) & x) ^ xor_bits(BitVecVal(0x1F80CE6109C6320060200E100C208, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x161B1831E960, 128) & x) ^ xor_bits(BitVecVal(0x2090810E020, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xE60, 128) & x) ^ xor_bits(BitVecVal(0x220, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x1, 128) & x) ^ xor_bits(BitVecVal(0x0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x27A34B, 128) & x) ^ xor_bits(BitVecVal(0x38101, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x2A6578A4772D94E8, 128) & x) ^ xor_bits(BitVecVal(0x20380033048060, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x27D96036E73482CFEBC7, 128) & x) ^ xor_bits(BitVecVal(0x3C8201263100047E1C3, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xF7D2916E7B86CBC1EAA90C6D91CBD6D2, 128) & x) ^ xor_bits(BitVecVal(0x33C00026398241C0E000042480C1C240, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x7F5C045BF883B66C591E7D63, 128) & x) ^ xor_bits(BitVecVal(0x1F0C0009F8019224080E3C21, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x230C9D771C, 128) & x) ^ xor_bits(BitVecVal(0x1040C330C, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x65C3, 128) & x) ^ xor_bits(BitVecVal(0xC1, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xA0F976CAC739C6EA72493E20A84D29, 128) & x) ^ xor_bits(BitVecVal(0x7832404318C26030001E00000400, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x5C14054D9, 128) & x) ^ xor_bits(BitVecVal(0xC0000048, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xB36DDC2F, 128) & x) ^ xor_bits(BitVecVal(0x1124CC07, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x204B38A49FB9E97EFBE5E5, 128) & x) ^ xor_bits(BitVecVal(0x118000F98E03E79E0E0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x2B98A6879C51C9A3FC98297AC62FAAE5, 128) & x) ^ xor_bits(BitVecVal(0x18802038C00C081FC08003842078060, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x102B85, 128) & x) ^ xor_bits(BitVecVal(0x180, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x13EEC3E9B7A62DE368BF9, 128) & x) ^ xor_bits(BitVecVal(0x1E641E0938204E1201F8, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x52225B78259, 128) & x) ^ xor_bits(BitVecVal(0x938008, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x16C9B2152C1AD870E0D19D979D3158F8, 128) & x) ^ xor_bits(BitVecVal(0x24090000408483060408C838C100878, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x3D6A84016CE76CDF9788C6CF07988A, 128) & x) ^ xor_bits(BitVecVal(0xC2000002463244F83804247038800, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x45B1FFF0C4B02E1605, 128) & x) ^ xor_bits(BitVecVal(0x90FFF04010060200, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x4A67A61, 128) & x) ^ xor_bits(BitVecVal(0x23820, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x6E, 128) & x) ^ xor_bits(BitVecVal(0x6, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1D6BB8D17902FB4A8F, 128) & x) ^ xor_bits(BitVecVal(0x42198403800790007, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x8FA1BC3BDBC15350, 128) & x) ^ xor_bits(BitVecVal(0x7809C19C9C00100, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x168, 128) & x) ^ xor_bits(BitVecVal(0x20, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xBBE7539D2F7076F4CE1FCC1EF3CA8EB, 128) & x) ^ xor_bits(BitVecVal(0x19E3018C07303270460FC40E71C0061, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x1DC9E17, 128) & x) ^ xor_bits(BitVecVal(0x4C0E03, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x8985169ABA2ED, 128) & x) ^ xor_bits(BitVecVal(0x80020818064, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xE1BBE1C0CD10, 128) & x) ^ xor_bits(BitVecVal(0x2099E0C04400, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x7D371F4E0F5F0BF3F03955, 128) & x) ^ xor_bits(BitVecVal(0x1C130F06070F01F1F01800, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x3C0E6DF6B620EE, 128) & x) ^ xor_bits(BitVecVal(0xC0624F2120066, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x856D694, 128) & x) ^ xor_bits(BitVecVal(0x24200, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xE, 128) & x) ^ xor_bits(BitVecVal(0x2, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1CF36E1E92DDA26711DF8173584D, 128) & x) ^ xor_bits(BitVecVal(0x471260E004C802300CF80310804, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x8CDA56927574B1719C6698849, 128) & x) ^ xor_bits(BitVecVal(0x4480200303010308C2208000, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x18F63280A9C35EAE, 128) & x) ^ xor_bits(BitVecVal(0x72100000C10E06, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x1B6D7D560CA2D83B5146C79, 128) & x) ^ xor_bits(BitVecVal(0x1243C02040048190002438, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x7D5EC3A7E8F086BD8C888FB73A7AA, 128) & x) ^ xor_bits(BitVecVal(0x1C0E4183E070021C8400079318380, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x3E, 128) & x) ^ xor_bits(BitVecVal(0xE, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x71C2EA7DD6721FBB31A755F7F0, 128) & x) ^ xor_bits(BitVecVal(0x10C0603CC2300F99108300F3F0, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x1D8006A2ECC30EC6E96060612484CB, 128) & x) ^ xor_bits(BitVecVal(0x48002006441064260202020000041, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x40D7BF53BD2B, 128) & x) ^ xor_bits(BitVecVal(0x439F019C01, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x31B9C7CE6BA08F87CBB, 128) & x) ^ xor_bits(BitVecVal(0x98C3C621800783C19, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x3, 128) & x) ^ xor_bits(BitVecVal(0x0, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x389D589A2F5D200CF, 128) & x) ^ xor_bits(BitVecVal(0x80C0808070C00047, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x66E1D, 128) & x) ^ xor_bits(BitVecVal(0x260C, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xAE40064FE2D6E6, 128) & x) ^ xor_bits(BitVecVal(0x6000207E04262, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0xB2F81EBF410D713BCBE, 128) & x) ^ xor_bits(BitVecVal(0x10780E1F00043019C1E, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xA487A1A738B137F6E5F0A12285E4, 128) & x) ^ xor_bits(BitVecVal(0x38083181013F260F0000000E0, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xEF2F92935FDEFCBF4D5822E, 128) & x) ^ xor_bits(BitVecVal(0x270780010FCE7C1F0408006, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x3A4B402B51711661FBC7B32BE93, 128) & x) ^ xor_bits(BitVecVal(0x801000100300220F9C39101E01, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x35DD6F859D74F, 128) & x) ^ xor_bits(BitVecVal(0xCC27808C307, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x21F407CE26E40E2, 128) & x) ^ xor_bits(BitVecVal(0xF003C60260060, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x375BF3A, 128) & x) ^ xor_bits(BitVecVal(0x309F18, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x181A253BC548D3FBC8AE18E23, 128) & x) ^ xor_bits(BitVecVal(0x80019C00041F9C00608601, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1F509272889043217CFC56, 128) & x) ^ xor_bits(BitVecVal(0x7000030000001003C7C02, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x22054C3647A1536F25EBA457C, 128) & x) ^ xor_bits(BitVecVal(0x4120380012700E18003C, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x153BFBDD, 128) & x) ^ xor_bits(BitVecVal(0x19F9CC, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x9D2B6EBF232E4A9, 128) & x) ^ xor_bits(BitVecVal(0xC01261F0106000, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xA52B, 128) & x) ^ xor_bits(BitVecVal(0x1, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x330D8F33869AED44B8A5255, 128) & x) ^ xor_bits(BitVecVal(0x1048711820864001800000, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x17CBFC73336, 128) & x) ^ xor_bits(BitVecVal(0x3C1FC31112, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x8D6AF39F592011E48AA7CB4B, 128) & x) ^ xor_bits(BitVecVal(0x420718F080000E00003C101, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x14A33B1D24F27764FB0656A8CF, 128) & x) ^ xor_bits(BitVecVal(0x1190C007033207902020047, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xED0C2, 128) & x) ^ xor_bits(BitVecVal(0x24040, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1480871DCFB0535517EBBFB28DCA6, 128) & x) ^ xor_bits(BitVecVal(0x30CC790010003E19F9004C02, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x56D11B00CAFA443E11C9393D655A2375, 128) & x) ^ xor_bits(BitVecVal(0x24009004078001E00C0181C20080130, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xD2CA4211947191AE2C9B9F70FE7, 128) & x) ^ xor_bits(BitVecVal(0x4000008030808604098F307E3, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x12EEF0835, 128) & x) ^ xor_bits(BitVecVal(0x6670010, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x297C1A8D8, 128) & x) ^ xor_bits(BitVecVal(0x3C08048, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xAF5FBBA6506348A9199FC, 128) & x) ^ xor_bits(BitVecVal(0x70F998200210000088FC, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x82FE3E666BB603B908E800, 128) & x) ^ xor_bits(BitVecVal(0x7E1E2221920198006000, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0xB72AF73E919A14C2273070ED5C1DC, 128) & x) ^ xor_bits(BitVecVal(0x1300731E00880040031030640C0CC, 128) & x & RotateRight(x,1)))
s.add(0 == xor_bits(BitVecVal(0x1FBD2A505F0CA679799, 128) & x) ^ xor_bits(BitVecVal(0x79C00000F040238388, 128) & x & RotateRight(x,1)))
s.add(1 == xor_bits(BitVecVal(0x1D0F097C310F92E3C2BF8D42, 128) & x) ^ xor_bits(BitVecVal(0x407003C10078061C01F8400, 128) & x & RotateRight(x,1)))
print(s.check())
model = s.model()
print(model)
Преобразовав найденные биты в строку, получаем первый ключ: ItWasJustAWarmUp.
Stage #2
bool __cdecl sub_42CEF0(char *a1)
{
bool result; // al
__int64 v2; // [esp+0h] [ebp-3Ch]
__int64 v3; // [esp+8h] [ebp-34h]
__int64 v4; // [esp+10h] [ebp-2Ch]
__int64 v5; // [esp+18h] [ebp-24h]
__int64 v6; // [esp+20h] [ebp-1Ch]
int v7; // [esp+28h] [ebp-14h]
int v8; // [esp+2Ch] [ebp-10h]
int v9; // [esp+30h] [ebp-Ch]
int v10; // [esp+34h] [ebp-8h]
int v11; // [esp+38h] [ebp-4h]
v6 = *(_QWORD *)a1;
v5 = *((_QWORD *)a1 + 1);
v4 = *((_QWORD *)a1 + 2);
v3 = *((_QWORD *)a1 + 3);
v2 = *((_QWORD *)a1 + 4);
(**off_444360)(off_444360, &v6, 0, off_44435C);
(**off_444360)(off_444360, &v5, 0, off_44435C);
(**off_444360)(off_444360, &v4, 0, off_44435C);
(**off_444360)(off_444360, &v3, 0, off_44435C);
(**off_444360)(off_444360, &v2, 0, off_44435C);
v11 = 0;
result = 0;
if ( v6 == qword_442780 )
{
v10 = 8;
if ( v5 == __PAIR__(*((_DWORD *)&qword_442780 + 3), *((_DWORD *)&qword_442780 + 2)) )
{
v9 = 16;
if ( v4 == __PAIR__(*((_DWORD *)&qword_442780 + 5), *((_DWORD *)&qword_442780 + 4)) )
{
v8 = 24;
if ( v3 == __PAIR__(*((_DWORD *)&qword_442780 + 7), *((_DWORD *)&qword_442780 + 6)) )
{
v7 = 32;
if ( v2 == __PAIR__(*((_DWORD *)&qword_442780 + 9), *((_DWORD *)&qword_442780 + 8)) )
result = 1;
}
}
}
}
return result;
}
Введённая строка представляется в виде пяти 64-битных чисел, результат преобразования которых сравнивается с массивом.

Прогулявшись под отладчиком, видим такую последовательность вызова функций:

Пока видно, что третий аргумент передаётся по цепочке вызовов, иногда складываясь по модулю 2 с единицей, иногда с очередным битом введённой строки (хотя некоторые биты, например, первый, пропускаются).
Посмотрим, где происходит модификация преобразуемого числа, поставив точку останова на запись в соответствующие байты памяти:

Т.е. бит числа заменяется на значение третьего аргумента, его предыдущее значение передаётся дальше (иногда инвертированное). Можно предположить, что происходит следующее преобразование:
a3 = xor_all_bits(input & x) ^ y;
output = ((input << 1) ^ z) | a3;
- x определяет биты, которые не были пропущены на первом этапе;
- y depends on how many times a unit has been added (modulo 2);
- z corresponds to the bits inverted in the second step.
To determine the unknown constants, we get the converted values for zero and numbers with one bit set. To do this, define a counter
extern i; i = 0;
and in the function, sub_428FD0
before the next round, set a breakpoint with a condition that will print the value of the number after the function call, set its next value and repeat the function call.print(Qword(Qword(ebp+8))), i=i+1, (i<64)?patch_qword(Qword(ebp+8), __int64(1) << i)^(eip=0x428FE0):0,0
The resulting values are:

From:
- x = 0x11CE9E8E6CF2B888
- y = 1
- z = 0x8E2B550A6AEDD63A
So in Python, the conversion function looks like this:
def xor_bits(x):
a = 0
while x != 0:
a ^= x & 1
x >>= 1
return a
def round(x):
x = ((x << 1) & 0xFFFFFFFFFFFFFFFF) | xor_bits(x & 0x8e2b550a6aedd63a) ^ 1
return x ^ 0x11CE9E8E6CF2B888
def encrypt(x):
for _ in range(64):
x = round(x)
return x
And the reverse to him:
def rev_round(x):
x ^= 0x11CE9E8E6CF2B888
a = x & 1
x >>= 1
if xor_bits(x & 0x8e2b550a6aedd63a) ^ 1 != a:
x |= 1 << 63
return x
def decrypt(x):
for _ in range(64):
x = rev_round(x)
return x
Applying the conversion to numbers from the array
qword_442780
, we get the desired string: YourReversingSkillsAreImpressive_zN2018 .
Day2. Blackanwyte
For help in preparing the assignment, thank Vlad Roskov from @leetmore .

When clicking on the link, we see the following:

We are asked to enter a certain promotional code. Let's see which files load when the page opens.

After looking through whitebox.js we find a function that checks the promotional code:

The
c
text is read into the variable ; It checks if it matches the regular expression "/ \ d + - \ w + /", so the promotional code should be of the form "1234-ABCD". Next, the number is converted to binary, zeros correspond to "N", and units - "Z". After rearranging the numbers from the array f
, in a cycle of 4 letters from the resulting string, the md5 hash is taken, and its first four characters are checked with the corresponding characters from the string "7177a294cfa7b53371776be5cfa74ddf".Для получения правильного числа напишем небольшой скрипт.

В результате получаем необходимое число, первую часть нашего промокода: 234082018.

Первую часть проверки мы прошли. Перейдём ко второй:

Видно, что возвращенное из функции
blackbox_check
значение проверяется, и если не произошло ошибки, и оно равно «elee757bc7fd00d5», то отправляется на сервер. Однако в файле whitebox.js нет упоминания blackbox_check
, значит пора открыть blackbox.js. После того, как текстовый редактор справился с открытием длинного файла, видим в нём необходимую нам функцию blackbox_check, а в ней - интересную строчку (22).

Погуглив, узнаём, что мы столкнулись с эмулятором, написанным на Javascript.
В переменной
code
хранятся байты исполняемого файла, в строке 32 производится запись промокода в память, в 33 строке задаётся адрес стэка. Из строки 41 видно, что файл запускается, начиная с адреса «0x080485E0». Сохраним исполняемый файл и посмотрим что в нём с помощью IDA.

Перейдём в blackbox_check. Здесь мы видим какую-то хэш-функцию. После нескольких неудачных попыток найти возможности для быстрого брута или другие уязвимости в алгоритме, замечаем, что буфер, в который копируется часть строки после тире, ограничен всего 256 символами.
blackbox_check

Проверяем с помощью checksec и видим, что стэк является исполняемым:
Примечание — на скриншоте и в тексте автора врайтапа есть расхождения насчет того, является ли стек исполняемым. В действительности, код исполнить можно.

Учитывая то, что мы уже знаем адрес в стэке, куда запишется наша строка, дело за малым — записать в нужный адрес байты из строки «e1ee757bc7fd00d5».

Через консоль отправляем получившуюся строку и забираем флаг:
Искомая строка



Искомая строка: Bl4CK_0r_wYT3_It5_z3r0Ni6hT

Day3. Pad me
Hey, this group still use an old-style messaging system. We assume members are in their thirties and such is their crypto. Try finding flaws there. Their messaging system is at 51.15.79.170 And you know what? We could intercept a piece of their authentication request.
You can make use of it ytW81KkHaGOnaqiG7Gr4AA==:XXnpfKJoUCufBm2ztTXGCN6wgqLeScU8+XlL7Co5oHg=
1. I have received 403 error after post “ytW81KkHaGOnaqiG7Gr4AA ==: XXnpfKJoUCufBm2ztTXGCN6wgqLeScU8 + XlL7Co5oHg =” string. If change some letters - still receive 403 error, it change letter count - receive 500 error. After some investigations I realized that first part sould be 16 bytes long in base64-decoded form, and second part must contain some blocks 16 bytes long each. Seems first part is IV and second part - encoded data in CBC mode, and according to task name “pad” it is padding oracle attack.
2. Lets change last byte in base64 decoded form of second part (01.php script, total 256 possibilities). For one case instead of “403” error I have received message “Sir, your message is incorrect or it is not 48 bytes long”
01.php script
3. Lets decrypt message from task using padding oracle attack (scripts 02.php and 02b.php) – I have received string “Never gonna give you up\nNever go”.
02.php script
$a = „ytW81KkHaGOnaqiG7Gr4AA==:XXnpfKJoUCufBm2ztTXGCN6wgqLeScU8+XlL7Co5oHg=“;
$a = explode(»:", $a);
$a[0] = base64_decode($a[0]);
$a[1] = base64_decode($a[1]);
file_put_contents(«01.bin», $a[0].$a[1]);
$s = $a[1];
$known = "";
for($i=1;$i<=16;$i++)
{
for($j=0;$j<256;$j++)
{
$iv = str_repeat(«A», 16-$i).((chr($j).$known) ^ str_repeat(chr($i), $i));
if (($r = send($iv, substr($s, 0, 16))) !== false)
{
echo $r."\r\n";
$known = chr($j).$known;
echo bin2hex($known)." ".($known ^ substr($a[0], -$i))."\r\n";
break;
}
}
if ($j == 256) die(«Error 1: {$i}»);
}
function send($a, $b)
{
$data = base64_encode($a).":".base64_encode($b);
$s = «POST / HTTP/1.0\r\n»;
$s.= «Host: 51.15.79.170\r\n»;
$s.= «Content-Length: ».strlen($data)."\r\n";
$s.= «User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0\r\n»;
$s.= "\r\n";
$s.= $data;
$r = "";
do {$socket = fsockopen(«51.15.79.170», 80);} while(!$socket);
fwrite($socket, $s);
while(!@feof($socket)) $r .= fread($socket, 4096);
fclose($socket);
if (strpos($r, «500 INTERNAL SERVER ERROR»)) die(«Error 2: {$data}»);
return (strpos($r, «403 FORBIDDEN») !== false)?false:$r;
}
Never gonna give you upHTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:49:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
0f0cc904c9a68b038e65 gonna give
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:49:25 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
270f0cc904c9a68b038e65 gonna give
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:50:10 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
db270f0cc904c9a68b038e65 r gonna give
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:51:11 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
b1db270f0cc904c9a68b038e65 er gonna give
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:52:15 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
cab1db270f0cc904c9a68b038e65 ver gonna give
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:53:19 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
b0cab1db270f0cc904c9a68b038e65 ever gonna give
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:54:15 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
84b0cab1db270f0cc904c9a68b038e65 Never gonna give
02b.php script
Never goHTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:50:12 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
67 o
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:51:11 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
a167 go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:51:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
15a167 go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:52:30 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
c715a167 r go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:53:57 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
d6c715a167 er go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:54:15 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
1bd6c715a167 ver go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:54:28 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
631bd6c715a167 ever go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:55:21 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
d1631bd6c715a167 Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:55:43 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
21d1631bd6c715a167
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:55:47 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
2021d1631bd6c715a167 p
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:55:51 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
1d2021d1631bd6c715a167 up
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:56:07 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
821d2021d1631bd6c715a167 up
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:56:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
09821d2021d1631bd6c715a167 u up
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:56:52 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
8609821d2021d1631bd6c715a167 ou up
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:56:52 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
008609821d2021d1631bd6c715a167 you up
Never go
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 21:57:10 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
7d008609821d2021d1631bd6c715a167 you up
Never go
4. Ok, seems the message was truncated (should be 48 bytes long). Using google I have found some song with full phase "Never gonna give you up\nNever gonna let you down”. Lets encrypt it using padding oracle attack from last block till first block and iv (scripts 05.php and 04_brute.php).
04_brute.php script
05.php
20c3540ec72fa0b486b5f0c868218360HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:51:49 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
60
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:52:05 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
8360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:52:15 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:52:52 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
68218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:53:56 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:55:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:56:09 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:57:05 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
86b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:58:03 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:58:59 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
a0b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 23 Oct 2018 23:59:05 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
2fa0b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 24 Oct 2018 00:00:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
c72fa0b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 24 Oct 2018 00:00:10 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
0ec72fa0b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 24 Oct 2018 00:00:21 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
540ec72fa0b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 24 Oct 2018 00:01:21 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
c3540ec72fa0b486b5f0c868218360
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 24 Oct 2018 00:01:34 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 57
Connection: close
Sir, your message is incorrect or it is not 48 bytes long
20c3540ec72fa0b486b5f0c868218360
Искомая строка64
94a0daa1cb371f1cd914d9b69b139e75
f05329a5bfdb4469f906bae4fd4104b1
ff4fba7f36536f04bd0a3f77b8b06dde
6ea6226bb50fc7dbe8db91e80f48f505
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 24 Oct 2018 00:01:51 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 73
Connection: close
Okey, I see you're from 80's, get in
<Wh4t_1f_R1ck_A3tl3y_Sm0ke_Cannab1s>