diff options
Diffstat (limited to 'informationals/teso-i0027.txt')
| -rw-r--r-- | informationals/teso-i0027.txt | 279 |
1 files changed, 279 insertions, 0 deletions
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 | |||
