flag_checker
So we are given a binary named flagchecker
, so i decided to decompile it.
undefined8 main(void)
{
char cVar1;
long in_FS_OFFSET;
undefined1 local_38 [40];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
get_input(local_38);
cVar1 = check_input(local_38);
if (cVar1 != '\0') {
puts("PRINTING FLAG: ");
print_flag(local_38);
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
void get_input(long param_1)
{
int local_10;
int local_c;
for (local_10 = 0; local_10 < 8; local_10 = local_10 + 1) {
printf("> ");
__isoc99_scanf(&DAT_001020a3,param_1 + (long)local_10 * 4);
}
for (local_c = 0; local_c < 8; local_c = local_c + 1) {
*(uint *)((long)local_c * 4 + param_1) = *(uint *)(param_1 + (long)local_c * 4) % 0xffffff2f;
}
return;
}
undefined8 check_input(long param_1)
{
int iVar1;
int local_10;
local_10 = 0;
while( true ) {
if (7 < local_10) {
return 1;
}
iVar1 = F(*(undefined4 *)(test_pt + (long)local_10 * 4),
*(undefined4 *)(param_1 + (long)local_10 * 4),0xffffff2f);
if (iVar1 != *(int *)(test_ct + (long)local_10 * 4)) break;
local_10 = local_10 + 1;
}
return 0;
}
ulong F(long param_1,ulong param_2,ulong param_3)
{
undefined8 local_28;
undefined8 local_18;
undefined8 local_10;
local_18 = 1;
local_10 = param_1 % (long)param_3;
for (local_28 = param_2; 0 < (long)local_28; local_28 = (long)local_28 >> 1) {
if ((local_28 & 1) != 0) {
local_18 = (local_18 * local_10) % param_3;
}
local_10 = (local_10 * local_10) % param_3;
}
return local_18;
}
You have to enter 8 numbers x[0]..x[7]
(each number is taken mod 0xffffff2f
)
F(test_pt[i], x[i], 0xffffff2f) == test_ct[i]
Then the value of x[0]..x[7]
will be used to decrypt flag_enc
function F:
ulong F(base, exp, mod) {
ulong res = 1;
base = base % mod;
while (exp > 0) {
if (exp & 1)
res = (res * base) % mod;
base = (base * base) % mod;
exp >>= 1;
}
return res;
}
That’s modular exponentiation:
F(a, b, p) = pow(a, b, p)
Find the array x[0..7] ∈ [0, 0xffffff2f)
so that:
pow(test_pt[i], x[i], 0xffffff2f) == test_ct[i]
In other words, we need to:
x[i] == discrete_log(test_pt[i], test_ct[i], 0xffffff2f)
First, we need to find the values of test_pt
and test_ct
from the memory dump by using signature flag_enc
from .rodata
section:
test_pt XREF[3]: Entry Point(*),
check_input:0010131f(*),
check_input:00101326(*)
00102040 f5 b1 65 undefine
22 4a 58
b7 91 df
test_ct XREF[3]: Entry Point(*),
check_input:00101349(*),
check_input:00101350(*)
00102060 5e bf 44 undefine
dc ec 1c
ff 5a c2
flag_enc XREF[3]: Entry Point(*),
print_flag:001013c4(*),
print_flag:001013cb(*)
00102080 11 91 18 undefine
24 45 e9
94 fd a6
from pwn import *
e = ELF('./flagchecker', checksec=False)
needle = bytes([0x11, 0x91, 0x18, 0x24, 0x45, 0xe9, 0x94, 0xfd])
addr = e.search(needle).__next__()
print(f"Found flag_enc at file offset: {hex(addr)}")
flag_enc_offset = addr
test_ct_offset = flag_enc_offset - 0x20
test_pt_offset = flag_enc_offset - 0x40
test_pt = [u32(e.read(test_pt_offset + i*4, 4)) for i in range(8)]
test_ct = [u32(e.read(test_ct_offset + i*4, 4)) for i in range(8)]
print("test_pt =", test_pt)
print("test_ct =", test_ct)
Output:
Found flag_enc at file offset: 0x2080
test_pt = [577090037, 2444712010, 3639700191, 3445702192, 3280387012, 271041745, 1095513148, 506456969]
test_ct = [3695492958, 1526668524, 3790189762, 20093842, 2409408810, 239453620, 1615481745, 1887562585]
We already have test_pt
, test_ct
, and mod = 0xffffff2f = 4294967087
, just solve the equation:
pow(test_pt[i], x[i], mod) == test_ct[i]
which means we need a discrete log:
x[i] = discrete_log(mod, test_ct[i], test_pt[i])
But because the mod is so big (32-bit prime), discrete_log will take a long time using the general method. So we use Baby-step Giant-step (BSGS) to find the discrete log (https://en.wikipedia.org/wiki/Baby-step_giant-step) This is much faster than brute-force and can handle large moduli like 0xffffff2f
Here is the implementation:
mod = 0xffffff2f
test_pt = [577090037, 2444712010, 3639700191, 3445702192, 3280387012, 271041745, 1095513148, 506456969]
test_ct = [3695492958, 1526668524, 3790189762, 20093842, 2409408810, 239453620, 1615481745, 1887562585]
from math import isqrt
def bsgs(g, h, p):
m = isqrt(p) + 1
table = {}
baby = 1
for j in range(m):
if baby not in table:
table[baby] = j
baby = (baby * g) % p
g_m_inv = pow(g, -m, p)
giant = h
for i in range(m):
if giant in table:
return i * m + table[giant]
giant = (giant * g_m_inv) % p
return None
x = []
for i in range(8):
xi = bsgs(test_pt[i], test_ct[i], mod)
if xi is None:
print(f"FAILED at index {i}")
else:
print(f"x[{i}] = {xi}")
x.append(xi)
Output:
x[0] = 2127877499
x[1] = 1930549411
x[2] = 2028277857
x[3] = 2798570523
x[4] = 901749037
x[5] = 1674216077
x[6] = 3273968005
x[7] = 3294916953
Solve
❯ ./flagchecker
> 2127877499
> 1930549411
> 2028277857
> 2798570523
> 901749037
> 1674216077
> 3273968005
> 3294916953
PRINTING FLAG:
sigpwny{CrackingDiscreteLogs4TheFun/Lols���}
sigpwny{CrackingDiscreteLogs4TheFun}