<style>
img[alt=chall-sc] {
display: block;
margin: 0 auto;
width: 100em;
}
p {
text-align: justify;
}
p::first-line {
text-indent: 0;
}
</style>
# ARA CTF 6.0 QUALIFICATION WRITEUP
## Sanity Check

Free flag
`Flag: ARA6{apakah_kalian_akan_memasak_atau_dimasak?????}`
## Simple Math

Given a disassembled Python bytecode and the output
```python
0 0 RESUME 0
2 2 LOAD_CONST 9 ((5,))
4 LOAD_CONST 1 (<code object conv at 0x000001D2B5453870, file "<string>", line 2>)
6 MAKE_FUNCTION 1 (defaults)
8 STORE_NAME 0 (conv)
6 10 PUSH_NULL
12 LOAD_NAME 1 (open)
14 LOAD_CONST 2 ('flag.txt')
16 CALL 1
24 LOAD_ATTR 5 (NULL|self + read)
44 CALL 0
52 STORE_NAME 3 (flag)
7 54 BUILD_LIST 0
56 STORE_NAME 4 (flags)
8 58 BUILD_LIST 0
60 LOAD_CONST 3 ((412881107802, 397653008560, 378475773842, 412107467700, 410815948500, 424198405792, 379554633200, 404975010927, 419449858501, 383875726561))
62 LIST_EXTEND 1
64 STORE_NAME 5 (N)
9 66 PUSH_NULL
68 LOAD_NAME 6 (reversed)
70 LOAD_NAME 5 (N)
72 CALL 1
80 STORE_NAME 7 (NR)
11 82 PUSH_NULL
84 LOAD_NAME 8 (len)
86 LOAD_NAME 3 (flag)
88 CALL 1
96 LOAD_CONST 0 (5)
98 BINARY_OP 6 (%)
102 LOAD_CONST 4 (0)
104 COMPARE_OP 40 (==)
108 POP_JUMP_IF_TRUE 2 (to 114)
110 LOAD_ASSERTION_ERROR
112 RAISE_VARARGS 1
13 >> 114 PUSH_NULL
116 LOAD_NAME 9 (zip)
118 PUSH_NULL
120 LOAD_NAME 0 (conv)
122 LOAD_NAME 3 (flag)
124 CALL 1
132 LOAD_NAME 5 (N)
134 LOAD_NAME 7 (NR)
136 CALL 3
144 GET_ITER
>> 146 FOR_ITER 71 (to 292)
150 UNPACK_SEQUENCE 3
154 STORE_NAME 10 (i)
156 STORE_NAME 11 (j)
158 STORE_NAME 12 (k)
14 160 LOAD_NAME 13 (int)
162 LOAD_ATTR 29 (NULL|self + from_bytes)
182 LOAD_NAME 10 (i)
184 LOAD_ATTR 31 (NULL|self + encode)
204 CALL 0
212 LOAD_CONST 5 ('big')
214 CALL 2
222 STORE_NAME 16 (x)
15 224 LOAD_NAME 16 (x)
226 LOAD_NAME 11 (j)
228 BINARY_OP 0 (+)
232 LOAD_CONST 6 (1337)
234 BINARY_OP 5 (*)
238 LOAD_NAME 12 (k)
240 BINARY_OP 12 (^)
244 STORE_NAME 17 (y)
16 246 LOAD_NAME 17 (y)
248 LOAD_CONST 7 (871366131)
250 BINARY_OP 23 (-=)
254 STORE_NAME 17 (y)
17 256 LOAD_NAME 4 (flags)
258 LOAD_ATTR 37 (NULL|self + append)
278 LOAD_NAME 17 (y)
280 CALL 1
288 POP_TOP
290 JUMP_BACKWARD 73 (to 146)
13 >> 292 END_FOR
19 294 PUSH_NULL
296 LOAD_NAME 19 (print)
298 LOAD_NAME 4 (flags)
300 CALL 1
308 POP_TOP
310 RETURN_CONST 8 (None)
Disassembly of <code object conv at 0x000001D2B5453870, file "<string>", line 2>:
2 0 RETURN_GENERATOR
2 POP_TOP
4 RESUME 0
3 6 LOAD_GLOBAL 1 (NULL + range)
16 LOAD_CONST 1 (0)
18 LOAD_GLOBAL 3 (NULL + len)
28 LOAD_FAST 0 (str)
30 CALL 1
38 LOAD_FAST 1 (l)
40 CALL 3
48 GET_ITER
>> 50 FOR_ITER 12 (to 78)
54 STORE_FAST 2 (i)
4 56 LOAD_FAST 0 (str)
58 LOAD_FAST 2 (i)
60 LOAD_FAST 2 (i)
62 LOAD_FAST 1 (l)
64 BINARY_OP 0 (+)
68 BINARY_SLICE
70 YIELD_VALUE 1
72 RESUME 1
74 POP_TOP
76 JUMP_BACKWARD 14 (to 50)
3 >> 78 END_FOR
80 RETURN_CONST 0 (None)
>> 82 CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
84 RERAISE 1
```
### Disassembled Bytecode Analysis
The provided disassembled bytecode indicates the following operations:
1. **Reading `flag.txt`**: The script opens and reads the contents of `flag.txt` into a variable named `flag`.
2. **Initialization of Lists**:
- `N`: A list containing ten large integers.
- `NR`: A reversed version of the list `N`.
3. **Assertion**: The script checks that the length of `flag` is a multiple of 5.
4. **Processing Loop**: The script iterates over the zipped result of a function `conv(flag)`, `N`, and `NR`. For each tuple `(i, j, k)`:
- Converts the string `i` to an integer `x` using `int.from_bytes(i.encode(), 'big')`.
- Applies the following transformation to compute `y`:
$$
y = ((x + j) \times 1337) \oplus k - 871366131
$$
- Appends `y` to a list named `flags`.
5. **Output**: Finally, the script prints the `flags` list.
### Reversing the Transformation
To reconstruct the original content of `flag.txt`, we need to reverse the transformation applied to each character. Given the transformation:
$$
y = ((x + j) \times 1337) \oplus k - 871366131
$$
We can solve for `x` as follows:
$$
x = \left( \frac{(y + 871366131) \oplus k}{1337} \right) - j
$$
### Solving It
This is the script that that I made to reverse the transformation:
```python
#!/usr/bin/env python3
N = [412881107802, 397653008560, 378475773842, 412107467700, 410815948500, 424198405792, 379554633200, 404975010927, 419449858501, 383875726561]
NR = list(reversed(N))
ct = [927365724618649, 855544946535839, 1075456339888851, 1051300489856216, 854566738228717, 862564607600557, 1107196607637040, 835104762026329, 1108826984434051, 843310935687105]
pt = b""
for y, j, k in zip(ct, N, NR):
x = ((y + 871366131) ^ k) // 1337 - j
pt += x.to_bytes((x.bit_length() + 7) // 8, 'big')
print(pt)
```

`Flag: ARA6{8yT3_c0d3_W1Th_51MPl3_m4th_15_345Y____R19ht?}`
## memory

Given two ELF (Executable and Linkable Format) 64-bit files: `memory` and `memory-v2`.
I used `diff` to compare the symbol tables of two ELF binaries (`memory` and `memory-v2`) and show their differences

`memory-v2` includes a reference to String::len() from Rust's alloc crate, whereas `memory` does not. I decompiled the `memory-v2` using IDA. This is the result:
### main
```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
return std::rt::lang_start(memory::main, argc, (u8 **)argv, 0);
}
```
### memory::main
```c
void __cdecl memory::main()
{
__int64 v0; // rax
void *v1; // rax
__int64 v2; // rax
__int64 v3; // rax
__int64 v4; // rdx
_str v5; // rdi
_str v6; // rdx
_str v7; // rdi
core::str::iter::Chars v8; // rax
unsigned __int16 v9; // ax
__int16 v10; // ax
_str v11; // rdi
core::str::iter::Chars v12; // rax
usize v13; // [rsp+10h] [rbp-218h]
unsigned __int8 v14; // [rsp+59h] [rbp-1CFh]
unsigned __int8 v15; // [rsp+5Ah] [rbp-1CEh]
core::fmt::Arguments v16; // [rsp+E8h] [rbp-140h] BYREF
core::fmt::Arguments v17; // [rsp+118h] [rbp-110h] BYREF
alloc::string::String self; // [rsp+148h] [rbp-E0h] BYREF
core::fmt::Arguments v19; // [rsp+160h] [rbp-C8h] BYREF
__int64 v20; // [rsp+190h] [rbp-98h]
core::result::Result<usize,std::io::error::Error> v21; // [rsp+198h] [rbp-90h] BYREF
__int64 v22; // [rsp+1A8h] [rbp-80h]
core::option::Option<char> v23; // [rsp+1B4h] [rbp-74h] BYREF
core::option::Option<char> v24[2]; // [rsp+1B8h] [rbp-70h] BYREF
u8 *end_or_len; // [rsp+1C0h] [rbp-68h]
unsigned int v26; // [rsp+1CCh] [rbp-5Ch]
char v27; // [rsp+1D2h] [rbp-56h]
char v28; // [rsp+1D3h] [rbp-55h]
core::option::Option<char> v29; // [rsp+1D4h] [rbp-54h] BYREF
core::option::Option<char> v30[2]; // [rsp+1D8h] [rbp-50h] BYREF
u8 *v31; // [rsp+1E0h] [rbp-48h]
alloc::string::String other; // [rsp+1E8h] [rbp-40h] BYREF
char v33; // [rsp+203h] [rbp-25h]
int v34; // [rsp+204h] [rbp-24h]
void *v35; // [rsp+208h] [rbp-20h]
std::io::error::Error v36; // [rsp+210h] [rbp-18h]
core::fmt::Arguments::new_const(&v16, (_str (*)[1])off_5A2A8);
std::io::stdio::_print();
core::fmt::Arguments::new_const(&v17, (_str (*)[1])off_5A2B8);
((void (__fastcall *)(core::fmt::Arguments *))std::io::stdio::_print)(&v17);
alloc::string::String::new(&self);
core::fmt::Arguments::new_const(&v19, (_str (*)[1])off_5A2C8);
std::io::stdio::_print();
std::io::stdio::stdout();
v20 = v0;
<std::io::stdio::Stdout as std::io::Write>::flush();
v35 = v1;
if ( v1 )
{
v36.repr.__0.pointer = v35;
core::result::unwrap_failed();
}
std::io::stdio::stdin();
v22 = v2;
std::io::stdio::Stdin::read_line();
*(_QWORD *)v21.gap0 = v3;
*(_QWORD *)&v21.gap0[8] = v4;
core::ptr::drop_in_place<core::result::Result<usize,std::io::error::Error>>(&v21);
alloc::string::String::pop((core::option::Option<char> *)&self, &self);
v5 = <alloc::string::String as core::ops::deref::Deref>::deref(&self);
v6.data_ptr = (u8 *)"ARA6{";
v6.length = 4LL;
if ( core::str::<impl str>::starts_with(v5, v6) )
{
v7 = <alloc::string::String as core::ops::deref::Deref>::deref(&self);
v8 = core::str::<impl str>::chars(v7);
*(_QWORD *)v24[0].gap0 = v8.iter.ptr.pointer;
end_or_len = v8.iter.end_or_len;
v23 = (core::option::Option<char>)core::iter::traits::iterator::Iterator::nth(
v24,
(core::str::iter::Chars *)&::self,
(usize)v8.iter.end_or_len);
if ( <core::option::Option<T> as core::cmp::PartialEq>::eq(&v23, (core::option::Option<char> *)"{") )
{
v26 = 5;
v27 = 0;
LABEL_9:
v28 = 0;
while ( 1 )
{
v15 = v28 + v27;
if ( __CFADD__(v28, v27) )
core::panicking::panic_const::panic_const_add_overflow();
LOBYTE(v9) = v15 / 0x1Au;
HIBYTE(v9) = v15 % 0x1Au;
v10 = HIBYTE(v9);
v14 = v10 + 64;
if ( __CFADD__((_BYTE)v10, 64) )
core::panicking::panic_const::panic_const_add_overflow();
v33 = v10 + 64;
v34 = v14;
v11 = <alloc::string::String as core::ops::deref::Deref>::deref(&self);
v12 = core::str::<impl str>::chars(v11);
*(_QWORD *)v30[0].gap0 = v12.iter.ptr.pointer;
v31 = v12.iter.end_or_len;
v29 = (core::option::Option<char>)core::iter::traits::iterator::Iterator::nth(
v30,
(core::str::iter::Chars *)v26,
(usize)v12.iter.end_or_len);
LODWORD(other.vec.buf.inner.cap.__0) = v14;
if ( core::cmp::PartialEq::ne(&v29, (core::option::Option<char> *)&other) )
break;
if ( v28 == -1 )
core::panicking::panic_const::panic_const_add_overflow();
++v28;
if ( v26 == -1 )
core::panicking::panic_const::panic_const_add_overflow();
++v26;
if ( v28 == 15 )
{
if ( v27 == -1 )
core::panicking::panic_const::panic_const_add_overflow();
++v27;
goto LABEL_9;
}
if ( v27 == 4 )
{
HIDWORD(other.vec.buf.inner.cap.__0) = (unsigned int)alloc::string::String::pop(
(core::option::Option<char> *)&self,
&other);
if ( <core::option::Option<T> as core::cmp::PartialEq>::eq(
(core::option::Option<char> *)&other.vec.buf.inner.cap.__0 + 1,
(core::option::Option<char> *)&::other) )
{
v13 = alloc::string::String::len(&self);
if ( v13 == -1LL )
core::panicking::panic_const::panic_const_add_overflow();
if ( v13 == 66 )
{
memory::yes();
core::ptr::drop_in_place<alloc::string::String>(&self);
return;
}
}
break;
}
}
}
}
memory::no();
core::ptr::drop_in_place<alloc::string::String>(&self);
}
```
### memory::yes
```c
void __cdecl memory::yes()
{
core::fmt::Arguments v0; // [rsp+8h] [rbp-30h] BYREF
core::fmt::Arguments::new_const(&v0, (_str (*)[1])off_5A298);
std::io::stdio::_print();
}
```
### memory::no
```c
void __cdecl memory::no()
{
core::fmt::Arguments v0; // [rsp+8h] [rbp-30h] BYREF
core::fmt::Arguments::new_const(&v0, (_str (*)[1])pieces);
std::io::stdio::_print();
}
```
This is a flag checker challenge. If we input the correct flag, the result is `Yes! This one you got it right!`, and if we input the incorrect flag, the result is `That is... wrong`.
### Observing the Flag Pattern
The flag follows this structure:
- **Prefix**: `"ARA6{"`
- **Grid-based character sequence**: (60 characters)
- Generated using an indexed ASCII transformation.
- **Suffix**: `"}"`
### Solving It
The flag is compared character by character at elf_base + 0x94be against our input using `<core::option::Option<T> as core::cmp::PartialEq>::eq(&v23, (core::option::Option<char> *)"{")`.

I manually checked each of the compared character to retrieve the full flag.

`Flag: ARA6{@ABCDEFGHIJKLMNABCDEFGHIJKLMNOBCDEFGHIJKLMNOPCDEFGHIJKLMNOPQD}`