Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timb-machine/f2e39d7f570551abec2e9388c598d618 to your computer and use it in GitHub Desktop.
Save timb-machine/f2e39d7f570551abec2e9388c598d618 to your computer and use it in GitHub Desktop.
CVE-2022-36768 for shits and giggles... (WIP)
We start by unpacking the patch. On this occasion it's shipped as an RTE file (an AIX specific backup format), so we need to unpack it on our AIX VM like so:
$ restore -T -f ../invscout.rte
/lpp_name
/usr
/usr/lpp
/usr/lpp/invscout.rte
/usr/lpp/invscout.rte/liblpp.a
/usr/lpp/invscout.rte/inst_root
/usr/lpp/invscout.rte/inst_root/liblpp.a
/usr/lpp/invscout.rte/inst_root/var/adm/invscout
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/lib
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/microcode
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/invs.mic.con.inp
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/invs.vpd.con.inp
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/invsd.puk
/usr/sbin/invscout
/usr/sbin/invscoutd
/opt/IBMinvscout
/opt/IBMinvscout/bin
/opt/IBMinvscout/sbin
/opt/IBMinvscout/bin/invscoutClient_VPD_Survey
/opt/IBMinvscout/bin/invscoutClient_PartitionID
/opt/IBMinvscout/sbin/invscout_lsvpd
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/cat.mic
/usr/lpp/invscout.rte/inst_root/var/adm/invscout/license.mic
This is done in preference to using installp as I don't want to overwrite the currently vulnerable versions.
Doing so, we see the following potential victims:
$ find . -perm -u+s -ls
4115 3 -r-sr-xr-x 1 root system 2428 May 13 2004 ./opt/IBMinvscout/bin/invscoutClient_PartitionID
4114 11504 -r-sr-xr-x 1 root system 11779340 Jun 30 2010 ./opt/IBMinvscout/bin/invscoutClient_VPD_Survey
4108 516 -r-sr-xr-x 1 root system 528363 Jul 28 15:56 ./usr/sbin/invscout
4109 576 -r-sr-x--- 1 root system 589086 Jul 28 15:56 ./usr/sbin/invscoutd
So how have the binaries changed?
$ for x in old/*
do
strings $x >$x.strings
done
$ for x in new/*
do
strings $x >$x.strings
done
Gives us the following:
$ ls -l old/*.strings new/*.strings
-rw-r--r-- 1 root root 132631 Dec 17 17:31 old/invscout.strings
-rw-r--r-- 1 root root 427 Dec 17 17:31 old/invscoutClient_PartitionID.strings
-rw-r--r-- 1 root root 8615059 Dec 17 17:31 old/invscoutClient_VPD_Survey.strings
-rw-r--r-- 1 root root 146880 Dec 17 17:31 old/invscoutd.strings
-rw-r--r-- 1 root root 132847 Dec 17 17:31 new/invscout.strings
-rw-r--r-- 1 root root 427 Dec 17 17:31 new/invscoutClient_PartitionID.strings
-rw-r--r-- 1 root root 8615059 Dec 17 17:31 new/invscoutClient_VPD_Survey.strings
-rw-r--r-- 1 root root 147120 Dec 17 17:31 new/invscoutd.strings
$ md5sum old/*.strings new/*.strings
801ebcf03ca7287fa0706b9f97a7bdc7 old/invscout.strings
2fe24e8ba87077d83ef19001d34e0163 old/invscoutClient_PartitionID.strings
5982247e3b6995b2281952a450e6e2a8 old/invscoutClient_VPD_Survey.strings
3bb0efec67fbfad61113944acd13cd2b old/invscoutd.strings
63c951be573df0f1b7de0a14a65eea89 new/invscout.strings
2fe24e8ba87077d83ef19001d34e0163 new/invscoutClient_PartitionID.strings
5982247e3b6995b2281952a450e6e2a8 new/invscoutClient_VPD_Survey.strings
ae459221d53bdb577f1a89c2dd9161c8 new/invscoutd.strings
$ grep "/22 " old/*.strings new/*.strings
new/invscout.strings:@(#)75 1.32.1.34 src/bos/usr/sbin/invscout/main_build/Initialz.c, cmdinvscout, 52is220, 2230A_52is220 7/27/22 10:18:57
new/invscout.strings:@(#)78 1.20 src/bos/usr/sbin/invscout/main_build/MainSwch.c, cmdinvscout, 52is220, 2230A_52is220 7/27/22 10:22:20
new/invscout.strings:@(#)27 1.8 src/bos/usr/sbin/invscout/main_build/InstallFlash.c, cmdinvscout, 52is220, 2230A_52is220 7/27/22 10:19:48
new/invscoutd.strings:@(#)75 1.32.1.34 src/bos/usr/sbin/invscout/main_build/Initialz.c, cmdinvscout, 52is220, 2230A_52is220 7/27/22 10:18:57
new/invscoutd.strings:@(#)78 1.20 src/bos/usr/sbin/invscout/main_build/MainSwch.c, cmdinvscout, 52is220, 2230A_52is220 7/27/22 10:22:20
new/invscoutd.strings:@(#)27 1.8 src/bos/usr/sbin/invscout/main_build/InstallFlash.c, cmdinvscout, 52is220, 2230A_52is220 7/27/22 10:19:48
So we can be fairly sure we want to focus on invscout and invscoutd. Remember, the advisory is for the former (wonder what is lurking in the latter!?!! - reading the invscoutd man page: "By default, an accompanying cleartext password is required from the client for most operations. If the client's password does not match the system password for the authentication user ID invscout, the action exits with a return code. The authentication user ID cannot be changed." - could be a fun target if anyone used it :)).
Anyway, looking at the strings from invscout (on a Linux host, the AIX strings binary is a bit more limited), we see:
* References to SUID_PROFILE_CHECK
* Introduction of calls to secure_popen()
* Introduction of calls to secure_system()
At this point we can leverage grace.sh (https://github.com/timb-machine/local/blob/develop/scripts/mine/grace.sh) to build up a more comprehensive view of what is occurring when we call invscout, but we will need to tweak it to handle AIX's calling convention (https://www.ibm.com/docs/en/aix/7.2?topic=overview-register-usage-conventions). Essentially, function parameters in AIX land are passed in $r3-$r10, so we want to have grace extract them on each breakpoint that is hit.
Given the changes in invscout, we likely looking for a change that affects system() or popen() usage, we can reduce the .gdbinit to focus on just the most interesting functions such as:
* getenv()
* setenv()
* ...open()
* system()
* ...exec...()
* chmod()
* ...printf()
* strcpy()
* memcpy()
There's a couple of bugs in the public version of grace.sh above (I guess I should test changes) and I've tweaked the parsing of objdump a bit so we end up with the following:
#!/bin/sh
BINFILENAME="${1}"
COMMANDLINE="${2}"
tempfilename="grace.$$"
printf "set pagination off\n" >"${tempfilename}"
objdump -d "${BINFILENAME}" | grep ":$" | tr -d . | cut -f 2 -d "<" | cut -f 1 -d ">" | grep -v "^\\$" | sort | uniq | egrep "getenv|open|system|exec|chmod|setenv|unlink|printf|strcpy|memcpy" | while read line
do
printf "break %s\n" "${line}" >>"${tempfilename}"
printf "commands\n" >>"${tempfilename}"
printf "info registers r3 r4 r5 r6 r7 r8 r9 r10\n" >>"${tempfilename}"
printf "x/1s \$r3\n" >>"${tempfilename}"
printf "x/1s \$r4\n" >>"${tempfilename}"
printf "x/1s \$r5\n" >>"${tempfilename}"
printf "x/1s \$r6\n" >>"${tempfilename}"
printf "x/1s \$r7\n" >>"${tempfilename}"
printf "x/1s \$r8\n" >>"${tempfilename}"
printf "x/1s \$r9\n" >>"${tempfilename}"
printf "x/1s \$r10\n" >>"${tempfilename}"
printf "continue\n" >>"${tempfilename}"
printf "end\n" >>"${tempfilename}"
done
printf "run %s\n" "${COMMANDLINE}" >>"${tempfilename}"
printf "quit\n" >>"${tempfilename}"
gdb -x "${tempfilename}" "${BINFILENAME}"
Note, you could also use dump -X any [-t|-T] ... on AIX and grep for 0x to extract the symbols, but there we go. It's always a bit hit and miss figuring out the best way to extract the symbols but whatever way we go, we want a reliable list of symbols from which grace.sh can generate an appropriate .gdbinit.
Running this, we get something like:
$ grace.sh /usr/sbin/invscout
GNU gdb (GDB) 7.12.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "powerpc-ibm-aix5.1.0.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/sbin/invscout...done.
Breakpoint 1 at 0x1000a6d4
...
Breakpoint 22 at 0x100085f0
...
Breakpoint 11, 0xd0114ab4 in getenv () from /usr/lib/libc.a(shr.o)
r3 0xd0494f84 3494465412^M
r4 0x0 0^M
r5 0x2ff22ffc 804401148^M
r6 0xd032 53298^M
r7 0x3bd8 15320^M
r8 0x0 0^M
r9 0xd90a8000 3641344000^M
r10 0x0 0^M
0xd0494f84 <wstrtos+7908>: "AIX_TZCACHE"
Here we can see a breakpoint being triggered on getenv("AIX_TZCACHE"). Not very interesting but it shows off the concept.
From here, we can try fuzzing the various parameters to see if we can find where our input is being handed to secure_popen() or secure_system() like so:
$ ./grace.sh /usr/sbin/invscout "-fl tmb_fl"
$ ./grace.sh /usr/sbin/invscout "-fl ../../../../../../tmp/tmb_tmp"
$ ./grace.sh /usr/sbin/invscout "-catl tmb_catl"
$ ./grace.sh /usr/sbin/invscout "-catl ../../../../../../tmp/tmb_tmp"
$ ./grace.sh /usr/sbin/invscout "-m tmb_machine"
$ ./grace.sh /usr/sbin/invscout "-m ../../../../../../tmp/tmb_tmp"
$ ./grace.sh /usr/sbin/invscout "-s tmb_serial"
$ ./grace.sh /usr/sbin/invscout "-s ../../../../../../tmp/tmb_tmp"
$ ./grace.sh /usr/sbin/invscout "-m tmb_machine -s tmb_serial"
$ ./grace.sh /usr/sbin/invscout "-m ../../../../../../tmp/tmb_tmp -s ../../../../../../tmp/tmb_tmp"
Sadly this didn't work which calls for more direct targetting. To do this, let's take a closer look at what has changed:
$ objdump -D new/invscout | egrep ":$|secure_"
...
100007d8 <.TransIni>:
100007f8 <.bf>:
10000a88: 48 00 98 41 bl 1000a2c8 <.secure_system>
...
10008610 <._Trace_appendFile>:
10008660: 48 00 1c 69 bl 1000a2c8 <.secure_system>
..
1000907c <.RunShCmd>:
100092ec: 48 00 0f dd bl 1000a2c8 <.secure_system>
...
1000a010 <.xml_VPD_Survey>:
1000a028 <.bf>:
1000a160: 48 00 01 69 bl 1000a2c8 <.secure_system>
...
10011874 <.Cpopen>:
10011890: 4b ff 89 b9 bl 1000a248 <.secure_popen>
100118d8 <.Csystem>:
100118ec: 4b ff 89 dd bl 1000a2c8 <.secure_system>
...
1002bc10 <.FlashMicrocode>:
1002bc38 <.bf>:
1002bcf0: 4b fd e5 d9 bl 1000a2c8 <.secure_system>
...
1002bf5c <.RunRPM>:
1002bf80 <.bf>:
1002bfcc: 4b fd e2 fd bl 1000a2c8 <.secure_system>
At this point, it's a bit more clear where the potentially vulnerable code is.
Let's try running invscout with some of those breakpoints set:
$ gdb /usr/sbin/invscount
...
(gdb) b TransIni
Breakpoint 1 at 0x100007f8: file ../../../../../../../src/bos/usr/sbin/invscout/main_build/Initialz.c, line 1381.
(gdb) b _Trace_appendFile
Breakpoint 2 at 0x1000862c
(gdb) b RunShCmd
Breakpoint 3 at 0x1000909c
(gdb) b xml_VPD_Survey
Breakpoint 4 at 0x100157e4: file ../../../../../../../src/bos/usr/sbin/invscout/main_build/MainSwch.c, line 616.
(gdb) b FlashMicrocode
Breakpoint 5 at 0x1002bc9c: file ../../../../../../../src/bos/usr/sbin/invscout/main_build/InstallFlash.c, line 285.
(gdb) b RunRPM
Breakpoint 6 at 0x1002bfe4: file ../../../../../../../src/bos/usr/sbin/invscout/main_build/InstallFlash.c, line 161.
We can use grace.sh's .gdbinit generator to apply these and bulk to see if we get a match, which we do, but only on the outer functions:
Breakpoint 5, 0x1000909c in RunShCmd ()
Breakpoint 6, TransIni (GenrlPar=0x20009790 <_$STATIC_BSS>) at ../../../../../../../src/bos/usr/sbin/invscout/main_build/Initialz.c:1381
Breakpoint 7, 0x1000862c in _Trace_appendFile ()
Given that examining the strings from $r3 doesn't any thing that looks like a command string with user controlled input, it seems likely that we need to reach the inner functions under MainSwch.c and/or InstallFlash.c.
At this stage, I figured I'd have a look at how inscout was being used beyond the man pages and found this:
* http://gibsonnet.net/blog/cgaix/html/MDS%20reports.html
This steered me towards a public manifest (https://www3.software.ibm.com/ibmdl/pub/software/server/firmware/catalog.mic) and the following syntax:
REC="HDR" WEBPATH= "http://download.boulder.ibm.com/ibmdl/pub/software/server/firmware" FTPSRVR="-" FTPPATH="-" CDPATH="/microcode" AIXDEST="/tmp/fwupdate" LICFILE="flicense.mic" LICDATE="20021024" CATDATE="202212161502"
REC="DEV" README="146lpi.html" TM="DRVS18D" REBOOT="N" LDBLTLVL="31363930" CDDIR="/146lpi/" PKGNAME="8/35 GB 146LPi SCSI Disk Drive" IMPACT="New" LSMFORM="2." SEV="NEW" RUNDIAG="N" VFORM="A" DESC="18/35 GB 146LPi SCSI Disk Drive -PN 53P3241/53P3242" DEVTYPE="146lpi" PN="53P3241" ISMAN="Y" RPMFILE="146lpi-1690.rpm" RELDATE="01/05/2005 05:46:33 AM" LSMLTLVL="31363930" CONC="Y"
REC="DEV" RPMFILE="146lpi-1690.rpm" RELDATE="01/05/2005 05:46:33 AM" LSMLTLVL="31363930" CONC="Y" PN="53P3242" ISMAN="Y" VFORM="A" DESC="18/35 GB 146LPi SCSI Disk Drive -PN 53P3241/53P3242" DEVTYPE="146lpi" RUNDIAG="N" IMPACT="New" LSMFORM="2." SEV="NEW" LDBLTLVL="31363930" CDDIR="/146lpi/" PKGNAME="8/35 GB 146LPi SCSI Disk Drive" README="146lpi.html" TM="DDYS36M" REBOOT="N"
REC="DEV" IMPACT="New" LSMFORM="2." SEV="NEW" LDBLTLVL="53323942" CDDIR="/146z10i/" PKGNAME="9/18/35GB 146Z10i SCSI Disk Drive" README="146z10i.html" REBOOT="N" TM="DGVS09U" RPMFILE="146z10i-S29B.rpm" RELDATE="01/05/2005 05:44:12 AM" LSMLTLVL="53323942" CONC="N" PN="08K0264" ISMAN="Y" VFORM="A" DESC="9/18/35GB 146Z10i SCSI Disk Drive-PN 08K0294/08K0304/08K0264" DEVTYPE="146z10i" RUNDIAG="N"
At this point, the use of invscout's -UF flag becomes a bit more clear. We can use it to take the results of the catalogue, determine any missing patches or other updates, apply them and reboot the system as necessary. At this point, we probably have sufficient idea to figure out where the vulnerability lies...
-- TBC'd... --
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment