She Shells C Shells
Description
For this challenge, we’re given a single executable, shell
.
$file shell
shell: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c8ab24eb713f3b6c9036da743112176b91e58f1b, for GNU/Linux 3.2.0, not stripped
Solution
Opening the file in Ghidra, I find a function called: func_flag
fgets((char *)&local_118,0x100,stdin);
for (local_c = 0; local_c < 0x4d; local_c = local_c + 1) {
*(byte *)((long)&local_118 + (long)(int)local_c) =
*(byte *)((long)&local_118 + (long)(int)local_c) ^ m1[(int)local_c];
}
local_14 = memcmp(&local_118,t,0x4d);
if (local_14 == 0) {
for (local_10 = 0; local_10 < 0x4d; local_10 = local_10 + 1) {
*(byte *)((long)&local_118 + (long)(int)local_10) =
*(byte *)((long)&local_118 + (long)(int)local_10) ^ m2[(int)local_10];
}
printf("Flag: %s\n",&local_118);
uVar1 = 0;
}
This code reads in input from stdin and stores it in local_118
, which is then XOR’d with m1
and stored back in local_118
.
local_118
is then compared with t
and if it matches, it will XOR with m2
.
Finally, the program prints out local_118
as the flag. So to solve for the flag, we just need to XOR t
and m2
, which we can just copy out of Ghidra as a bytestring and use something like Cyberchef to XOR the values together.
t = 2c4ab799a3e57078936e97d9476d38bdffbb85996fe14aab74c37ba8b29fd7ecebcd63b23923e184929609c699f258facb6f6f5e1fbe2b138ea5a99993ab8f701cc0c43ea6fe933590c3c910e9
m2 = 641ef5e2c097441bf85ff9be185d488e91e4f6f15c8d269e2ba102f7c6f7e4b398fe57ed4a4bd1f6a1eb09c699f258facb6f6f5e1fbe2b138ea5a99993ab8f701cc0c43ea6fe933590c3c910e9
Flag: HTB{cr4ck1ng_0p3n_sh3ll5_by_th3_s34_sh0r3}
Needle in a Haystack
Description
Solution
Straightforward grep to win challenge, run strings on the binary and grep for the flag prefix.
$strings haystack | grep HTB
HTB{d1v1ng_1nt0_th3_d4tab4nk5}
Flag: HTB{d1v1ng_1nt0_th3_d4tab4nk5}
Cave System
Description
Solution
Opening the file in Ghidra, we see a very long block of conditional checks against our input:
printf("What route will you take out of the cave? ");
fgets((char *)&local_88,0x80,stdin);
iVar1 = memcmp(&local_88,&DAT_00102033,4);
if (((((((iVar1 == 0) && ((byte)(local_78._5_1_ * (char)local_58) == '\x14')) &&
((byte)((byte)local_68 - local_68._4_1_) == -6)) &&
(((((((byte)(local_68._5_1_ - local_70._2_1_) == -0x2a &&
((byte)((byte)local_78 - (char)local_58) == '\b')) &&
(((char)(local_58._7_1_ - (char)local_80) == -0x2b &&
(((byte)(local_70._2_1_ * local_88._7_1_) == -0x13 &&
((char)(local_88._4_1_ * (char)local_70) == -0x38)))))) &&
((local_68._2_1_ ^ local_70._4_1_) == 0x55)) &&
(((((byte)(local_70._6_1_ - local_58._7_1_) == '4' &&
((byte)(local_50._3_1_ + local_58._2_1_) == -0x71)) &&
((byte)(local_60._4_1_ + local_70._3_1_) == -0x2a)) &&
(((local_78._1_1_ ^ local_80._6_1_) == 0x31 &&
((byte)((byte)local_50 * local_78._4_1_) == -0x54)))))) &&
(((((byte)(local_50._2_1_ - local_70._2_1_) == -0x3e &&
(((local_70._2_1_ ^ local_88._6_1_) == 0x2f &&
((local_80._6_1_ ^ local_68._7_1_) == 0x5a)))) &&
((local_60._4_1_ ^ local_68._7_1_) == 0x40)) &&
((((((byte)local_60 == local_70._2_1_ &&
((byte)(local_78._7_1_ + local_58._1_1_) == -0x68)) &&
((byte)(local_78._7_1_ * local_50._3_1_) == 'h')) &&
(((byte)(local_88._1_1_ - local_70._4_1_) == -0x25 &&
((byte)((char)local_70 - local_70._5_1_) == -0x2e)))) &&
(((char)(local_68._6_1_ - (char)local_70) == '.' &&
((((byte)local_68 ^ local_78._6_1_) == 0x1a &&
((byte)(local_60._4_1_ * local_88._4_1_) == -0x60)))))))))))) &&
((((((byte)(local_68._6_1_ * local_70._3_1_) == '^' &&
((((byte)(local_80._7_1_ - (byte)local_60) == -0x38 &&
((local_58._1_1_ ^ local_58._5_1_) == 0x56)) &&
((local_70._2_1_ ^ local_60._5_1_) == 0x2b)))) &&
((((((local_58._6_1_ ^ local_80._1_1_) == 0x19 &&
((byte)(local_70._4_1_ - local_60._7_1_) == '\x1a')) &&
(((byte)(local_58._2_1_ + local_78._3_1_) == -0x5f &&
(((byte)(local_68._5_1_ + local_50._1_1_) == 'V' &&
((local_70._5_1_ ^ local_78._2_1_) == 0x38)))))) &&
((local_60._4_1_ ^ local_50._4_1_) == 9)) &&
((((((char)(local_80._7_1_ * local_68._6_1_) == 'y' &&
((local_68._5_1_ ^ local_70._6_1_) == 0x5d)) &&
((byte)(local_88._2_1_ * (byte)local_68) == '\\')) &&
(((byte)(local_80._2_1_ * local_78._2_1_) == '9' && (local_70._5_1_ == local_78._5_1_))
)) && (((byte)(local_68._3_1_ * local_78._5_1_) == '/' &&
(((byte)((char)local_80 * local_68._5_1_) == -0x55 &&
((byte)(local_68._7_1_ + local_70._2_1_) == -0x6d)))))))))) &&
(((((((local_70._2_1_ ^ local_68._2_1_) == 0x73 &&
((((local_78._4_1_ ^ local_70._7_1_) == 0x40 &&
((byte)(local_70._1_1_ + (byte)local_78) == -0x57)) &&
((local_68._7_1_ ^ local_50._3_1_) == 0x15)))) &&
((((byte)((byte)local_88 + local_50._3_1_) == 'i' &&
((byte)(local_68._2_1_ + local_60._6_1_) == -0x5b)) &&
(((local_70._6_1_ ^ local_58._4_1_) == 0x37 &&
(((byte)((byte)local_88 * local_70._4_1_) == '\b' &&
((byte)(local_68._2_1_ - (byte)local_50) == -0x3b)))))))) &&
((byte)(local_78._2_1_ + local_50._4_1_) == -0x1c)) &&
(((((local_68._3_1_ ^ (byte)local_60) == 0x6e &&
((byte)((byte)local_50 * (byte)local_78) == -0x54)) &&
((byte)(local_58._6_1_ - local_60._7_1_) == '\r')) &&
((((byte)(local_70._6_1_ + local_58._7_1_) == -100 &&
((byte)(local_88._6_1_ + local_68._1_1_) == -0x2c)) &&
(((byte)(local_88._7_1_ * local_70._5_1_) == -0x13 &&
((((byte)local_50 ^ local_70._5_1_) == 0x38 &&
((byte)(local_88._1_1_ * local_68._5_1_) == 'd')))))))))) &&
((((byte)local_50 ^ local_50._2_1_) == 0x46 &&
(((((((char)(local_88._2_1_ * local_78._3_1_) == '&' &&
((local_70._2_1_ ^ local_78._6_1_) == 0x2b)) &&
((byte)(local_88._1_1_ + local_88._7_1_) == -0x79)) &&
(((local_70._3_1_ ^ (byte)local_88) == 0x2a &&
((byte)(local_78._5_1_ - local_88._1_1_) == '\v')))) &&
((byte)(local_70._3_1_ + local_58._6_1_) == -0x32)) &&
(((local_78._1_1_ ^ local_80._5_1_) == 0x3b &&
((byte)(local_78._3_1_ - local_50._2_1_) == '\x12')))))))))) &&
((((local_78._1_1_ == local_80._2_1_ &&
((((byte)(local_80._6_1_ - local_50._2_1_) == 'M' &&
((byte)(local_60._2_1_ * local_58._4_1_) == 'N')) && (local_58._2_1_ == (byte)local_68)
))) && (((local_60._7_1_ ^ local_58._3_1_) == 0x38 &&
((char)(local_68._6_1_ + local_70._1_1_) == -0x6c)))) &&
((byte)(local_60._1_1_ + local_58._4_1_) == -0x31)))))) &&
((((local_60._4_1_ == local_78._4_1_ && ((char)(local_80._4_1_ + local_70._1_1_) == 'f')) &&
(((byte)(local_50._4_1_ + local_68._4_1_) == -0xf &&
((((byte)(local_60._1_1_ - local_78._5_1_) == '\x11' &&
((byte)(local_68._4_1_ - local_58._1_1_) == 'D')) &&
((byte)(local_80._1_1_ - local_68._3_1_) == 'D')))))) &&
((((local_58._5_1_ ^ local_58._3_1_) == 1 && ((local_68._2_1_ ^ local_50._1_1_) == 0xd)) &&
((((byte)(local_80._3_1_ - local_70._4_1_) == -0x15 &&
(((((char)(local_78._7_1_ + (char)local_70) == -0x67 &&
((byte)((char)local_70 + local_80._5_1_) == -0x6b)) &&
(((byte)(local_80._4_1_ - (byte)local_88) == -0x17 &&
(((((byte)(local_68._2_1_ + local_70._7_1_) == '`' &&
((byte)(local_88._5_1_ + local_58._5_1_) == -0x6a)) &&
((byte)(local_58._1_1_ * local_60._2_1_) == '`')) &&
(((byte)((char)local_58 * local_78._5_1_) == '\x14' &&
((byte)(local_70._3_1_ - local_58._4_1_) == '\x03')))))))) &&
((byte)(local_50._1_1_ + local_78._4_1_) == -0x6b)))) &&
((((byte)(local_80._2_1_ * local_58._5_1_) == -0x26 &&
((byte)(local_88._1_1_ + local_60._1_1_) == -0x3c)) &&
(((byte)(local_60._7_1_ - local_88._1_1_) == '\v' &&
(((local_60._3_1_ == local_78._3_1_ && ((byte)(local_68._7_1_ + local_60._7_1_) == -0x6d)
) && ((byte)(local_80._4_1_ * local_50._2_1_) == 'Q')))))))))))))) &&
(((((byte)((char)local_80 * local_70._2_1_) == 'A' &&
((byte)(local_60._6_1_ - local_70._7_1_) == 'E')) &&
((byte)(local_88._7_1_ + local_68._5_1_) == 'h')) &&
(((((char)(local_68._4_1_ + local_88._4_1_) == -0x44 &&
((byte)(local_70._7_1_ + (byte)local_68) == -0x5e)) &&
(((char)(local_70._1_1_ + local_88._5_1_) == 'e' &&
((((byte)(local_60._3_1_ * local_70._5_1_) == -0x13 &&
((local_80._5_1_ ^ local_60._5_1_) == 0x10)) &&
((char)((char)local_58 - local_80._4_1_) == ';')))))) &&
(((((char)(local_78._7_1_ - (char)local_80) == '\t' &&
((local_88._7_1_ ^ local_60._2_1_) == 0x41)) &&
((char)(local_88._5_1_ - local_60._3_1_) == -3)) &&
(((((local_50._4_1_ ^ local_78._2_1_) == 0x1a && ((local_88._1_1_ ^ local_88._3_1_) == 0x2f)
) && (((byte)(local_78._1_1_ - local_68._7_1_) == '+' &&
(((((byte)((char)local_80 + local_78._4_1_) == -0x2d &&
((byte)(local_80._3_1_ * local_58._5_1_) == -0x28)) &&
((byte)(local_70._3_1_ + local_88._6_1_) == -0x2e)) &&
(((byte)(local_88._5_1_ + local_88._3_1_) == -0x55 &&
((byte)(local_68._3_1_ - local_60._7_1_) == -0x2e)))))))) &&
(((byte)local_78 ^ local_68._1_1_) == 0x10)))))))))) {
puts("Freedom at last!");
}
else {
puts("Lost in the darkness, you\'ll wander for eternity...");
}
return 0;
}
It’s definitely possible to solve this by hand, but I would rather not. So instead, I use Angr.
Angr is a binary analysis platform written in Python and can perform symbolic execution, which we can use to solve the conditional and give us our flag.
Majority of the code below is just setting up angr to work with the binary. All you really need to provide is the address you want angr to reach, (in this case, we want to reach the “Freedom at last!” string).
From there we can just run the script and angr will find the input that satisfies the criteria.
import angr
import sys
p = angr.Project("cave")
good = (0x401aba)
initial_state = p.factory.entry_state()
simulation = p.factory.simgr(initial_state)
simulation.explore(find=good)
if simulation.found:
solution_state = simulation.found[0]
solution = solution_state.posix.dumps(sys.stdin.fileno())
print(solution.decode("utf-8"))
else:
raise Exception("Could not find solution")
$python3 solve.py
WARNING | 2023-03-30 15:18:58,870 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.
HTB{H0p3_u_d1dn't_g3t_th15_by_h4nd,1t5_4_pr3tty_l0ng_fl4g!!!}
Flag: HTB{H0p3_u_d1dn't_g3t_th15_by_h4nd,1t5_4_pr3tty_l0ng_fl4g!!!}