├── Attacking_Clientside_JIT_Compilers.pdf ├── Attacking_Clientside_JIT_Compilers_Paper.pdf ├── BSidesNola_Offense_At_Scale.pdf ├── CVE-2009-3608-explained.txt ├── Effective_Memory_Safety_Mitigations.pdf ├── Exploring_the_STL_Owning_erase.pdf ├── LeafSR_NaCl_Slides_BlackHat_2012.pdf ├── LeafSR_NaCl_Whitepaper_BlackHat_2012.pdf ├── NaCl_Summary-Team-CJETM.pdf ├── README.md ├── Ransomware_Is_Not_APT.pdf ├── Ruby-For-Pentesters.pdf └── Self-Protecting-GOT.html /Attacking_Clientside_JIT_Compilers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/Attacking_Clientside_JIT_Compilers.pdf -------------------------------------------------------------------------------- /Attacking_Clientside_JIT_Compilers_Paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/Attacking_Clientside_JIT_Compilers_Paper.pdf -------------------------------------------------------------------------------- /BSidesNola_Offense_At_Scale.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/BSidesNola_Offense_At_Scale.pdf -------------------------------------------------------------------------------- /CVE-2009-3608-explained.txt: -------------------------------------------------------------------------------- 1 | (Some details from this writeup taken from the Matasano blog http://chargen.matasano.com) 2 | 3 | Poppler contains an integer overflow vulnerability in version 0.11.0 that allows fewer class instances to be allocated then expected. 4 | This results in memory corruption when a constructor for each requested class instance is called. 5 | 6 | In poppler/XRef.cc on line 132 in the function ObjectStream::ObjectStream() the variable nObjects is an attacker controlled signed 7 | integer pulled directly from a PDF document. 8 | 9 | [132] if (nObjects >= INT_MAX / (int)sizeof(int)) { 10 | [133] error(-1, "Invalid 'nObjects'"); 11 | [134] goto err1; 12 | [135] } 13 | [136] 14 | [137] objs = new Object[nObjects]; 15 | 16 | On line 137 we see an array of Object classes being allocated using this attacker controlled value. If we look back up on line 132 17 | there was an attempt to detect too large of a value, and bail accordingly. The size of our class is 12 bytes (its definition is in 18 | poppler/Object.h). This means we can supply a value that will wrap around and cause only a few classes to be allocated. The value 19 | we need to supply should be less than (INT_MAX / (int)sizeof(int)) and large enough to overflow when we reach the new operator. 20 | 357913943 should do the trick and will cause new to allocate only 8 class instances. This can be easily triggered by a PDF with 21 | a compressed stream: 22 | 23 | ... 24 | /Type/ObjStm/N 357913943/First 2/Filter/FlateDecode/Length 1 25 | ... 26 | 27 | Our class has a constructor, and that constructor is shown below: 28 | ... 29 | class Object { 30 | public: 31 | // clear the anonymous union as best we can -- clear at least a pointer 32 | void zeroUnion() { this->name = NULL; } 33 | 34 | // Default constructor. 35 | Object(): 36 | type(objNone) { zeroUnion(); } 37 | ... 38 | 39 | Like most constructors it initializes some member variables of our class instance(s). It first sets the object type to objNone (0xd) 40 | and then it calls a small function, zeroUnion(), which initializes a union member within the class to NULL. But heres the tricky 41 | part, our Object constructor is called for all 357913943 class instances, not the 8 we tricked it into allocating. You wouldn't know 42 | this from looking at source. This is one of those times were binary analysis is needed, regardless of whether you have the code or 43 | not. And of course this is going to vary between different compilers. This little detail presents a major problem for our chances of 44 | successful exploitation. The entire constructor is small enough to be inlined by our compiler. Lets disassemble and reverse the 45 | relevant vulnerable code. This disassembly is from the ObjectStream::ObjectStream() function and occurs just after the call to the 46 | new operator: 47 | 48 | ... 49 | ee067: call 50904 ; call the new operator to allocate our class instances 50 | ee06c: test %esi,%esi ; %esi holds the # of Objects requested, check if its 0 51 | ee06e: je ee08f ; if we requested 0 objects theres no need to call any constructors, jump ahead 52 | ee070: mov %eax,%edx ; get the return value of new, our first class instance and move to %edx 53 | ee072: xor %ecx,%ecx ; clear %ecx 54 | ee074: lea 0x0(%esi,%eiz,1),%esi 55 | ee078: add $0x1,%ecx ; increment %ecx by 1 56 | ee07b: movl $0xd,(%edx) ; set this class instances object type to objNone 0xd 57 | ; (%edx points into a class instance on the heap) 58 | ee081: movl $0x0,0x4(%edx) ; move our 0x0 (NULL) value 4 bytes from %edx (this->name) 59 | ; (%edx points at a class instance on the heap) 60 | ee088: add $0xc,%edx ; add 12 to our class instance pointer (12 bytes is the size of our Object class) 61 | ee08b: cmp %ecx,%esi ; compare %ecx (number of class instances we initialized) 62 | ; and %esi (the number we need to call a constructor for) 63 | ee08d: jne ee078 ; if we aren't done yet, jump back and initialize the next class instance 64 | ... 65 | 66 | And our registers from the crash at runtime (tested using evince on Ubuntu, which uses libpoppler): 67 | 68 | eax 0x914cfe8 152358888 69 | ecx 0x2c03 11267 ; number of constructors we called 70 | edx 0x916e000 152494080 ; location of current class instance we are calling a constructor for (top of heap) 71 | ebx 0xb719bff4 -1223049228 72 | esp 0xb67a5ed0 0xb67a5ed0 73 | ebp 0xb67a5f68 0xb67a5f68 74 | esi 0x15555556 357913942 ; number of class instances we requested from new 75 | edi 0x914cc88 152358024 76 | eip 0xb70e88c3 0xb70e88c3 77 | 78 | The location of the heap in our evince process: 79 | 80 | $ cat /proc/`pidof evince`/maps | grep heap 81 | 08edf000-0916e000 rw-p 08edf000 00:00 0 [heap] 82 | 83 | The disassembly comments above are pretty self explanitory. As you can see we call 11267 constructors before we crash. Our constructor 84 | was called 11267 times basically just ripping through the heap writing 0xd and NULL bytes at every 12th dword. 85 | 86 | chris . rohlf at gmail.com (2009) 87 | -------------------------------------------------------------------------------- /Effective_Memory_Safety_Mitigations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/Effective_Memory_Safety_Mitigations.pdf -------------------------------------------------------------------------------- /Exploring_the_STL_Owning_erase.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/Exploring_the_STL_Owning_erase.pdf -------------------------------------------------------------------------------- /LeafSR_NaCl_Slides_BlackHat_2012.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/LeafSR_NaCl_Slides_BlackHat_2012.pdf -------------------------------------------------------------------------------- /LeafSR_NaCl_Whitepaper_BlackHat_2012.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/LeafSR_NaCl_Whitepaper_BlackHat_2012.pdf -------------------------------------------------------------------------------- /NaCl_Summary-Team-CJETM.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/NaCl_Summary-Team-CJETM.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Research, papers, slides 2 | 3 | I've published various white papers and slide decks over the years. This is where they will live out their days... 4 | 5 | If you have any questions about the content of these please email me 6 | 7 | chris dot rohlf at gmail dot com 8 | -------------------------------------------------------------------------------- /Ransomware_Is_Not_APT.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/Ransomware_Is_Not_APT.pdf -------------------------------------------------------------------------------- /Ruby-For-Pentesters.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/struct/research/5c4134012c86c6acbd6ad1deae530856a6051853/Ruby-For-Pentesters.pdf -------------------------------------------------------------------------------- /Self-Protecting-GOT.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 |



18 |

19 |

Self Protecting Global Offset Table 20 | (GOT)

http://em386.blogspot.com
24 August 2008
Draft 21 | Version 1.4

22 |

Introduction

This paper describes a simple technique 23 | for Executable Linkable Format (ELF) objects to protect themselves 24 | from a potential exploitation technique of unknown vulnerabilities by 25 | marking specific pages read-only from user space virtual memory. This 26 | technique requires no kernel patching or third party programs. GOT 27 | protection already exists in the form of PT_RELRO marked program 28 | headers and can be achieved using the GNU Linker (ld) flag -z 29 | and the keyword 'relro'. However sometimes these features may 30 | not be available on your target platform, and sometimes it may be 31 | worth exploring other ways to achieve them. That is the purpose of 32 | this paper, to explore another way to implement a read-only Global 33 | Offset Table.

It is assumed that the reader possesses some 34 | knowledge of C, ELF, binutils and the Linux operating system. All 35 | research was performed on a modern Ubuntu Linux 8.04 (Hardy Heron) 36 | operating system. Also it should be noted that this article mentions 37 | two different 'linkers'. One is the GNU linker (ld) which is 38 | generates ELF objects at compile time. The other is the dynamic 39 | linker (ld-linux) which is used at runtime.

Constructor 40 | Functions (ctors)

A constructor function in basic terms is 41 | a function that runs prior to another function. The term 42 | 'constructors' is usually associated with C++ programs. Classes 43 | usually execute constructors upon the creation of a new instance. 44 | However generic constructors are entirely possible to implement in C 45 | using a GCC attribute:

__attribute__((constructor)) void 46 | foo_func()

47 |

The function foo_func() 48 | will execute before main()

We can test this with 49 | a simple program:

50 |
# cat test.c
 51 | __attribute__((constructor)) void foo_func()
 52 | {
 53 |         printf("Hello from foo_func\n");
 54 | }
 55 | 
 56 | int main()
 57 | {
 58 |         printf("Hello from main\n");
 59 | 
 60 | return 0;
 61 | }
 62 | 
 63 | # gcc -o test test.c
 64 | # ./test 
 65 | Hello from foo_func
 66 | Hello from main

67 | You can read more about GCC attributes here 68 | http://www.ohse.de/uwe/articles/gcc-attributes.html. 69 | The technique covered in this paper occurs in a constructor function 70 | that is executed before main(). This allows our protection 71 | code to be added to preexisting code bases without any major 72 | modifications.

Despite our protection functions being 73 | executed before main() there are certain things in memory we simply 74 | cannot alter. If our protections become to invasive we run into 75 | runtime errors, which creates stability issues. Just like full kernel 76 | level protections such as PAX and Exec-Shield, we want to avoid 77 | disrupting program runtimes at all costs.

Runtime GOT PLT 78 | Protection

The GOT (Global Offset Table) is used by 79 | dynamically linked ELF objects. The ELF 1.2 specification states the 80 | following about the ELF relocation process:

"Relocation 81 | is the process of connecting symbolic references with symbolic 82 | definitions. For example, when a program calls a function, the 83 | associated call instruction must transfer control to the proper 84 | destination address at execution. In other words, relocatable files 85 | must have information that describes how to modify their section 86 | contents, thus allowing executable and shared object files to hold 87 | the right information for a process's program image. Relocation 88 | entries are these data."

If you want to read more 89 | about dynamic ELF relocation I have written a decent overview on my 90 | blog. 91 |

By default on modern Linux systems relocation is done the 92 | lazy way (RTLD_LAZY) by the dynamic linker (ld-linux). What this 93 | means is a function's location is only looked up when that function 94 | is first called. Every function in a loaded shared library that the 95 | program calls has a GOT entry. The default location in the GOT will 96 | point to the dynamic linker itself until the function is called, then 97 | the GOT is rewritten with the actual location of the function. This 98 | is mainly for performance reasons. However the dynamic linker can be 99 | forced to lookup the absolute location of all functions when the 100 | program is first executed. The way to force this is using the 101 | environment variable LD_BIND_NOW. Setting it to a non NULL value will 102 | force the dynamic linker to lookup all function locations immediately 103 | after all shared libraries are loaded and fill in the GOT with the 104 | correct values.

The GOT is an easy target for exploit writers. 105 | When an arbitrary 4 byte overwrite is possible anywhere in memory an 106 | attacker can overwrite a pointer in the GOT for a function that has 107 | not yet been executed, such as exit(). When the program 108 | finally calls exit() it's PLT entry will reference it's offset 109 | in the GOT and use its value as the location of the exit() 110 | function. This is usually overwritten with an attacker supplied 111 | function such as system() or an indirect execution of 112 | shellcode.

The first implementation of self GOT protection 113 | comes in the form of a special constructor function that checks for 114 | the LD_BIND_NOW environment variable, locates it's own GOT in memory 115 | and marks the memory segment read-only. My first implementation did 116 | not work too well for various reasons. For one I was not controlling 117 | the location of the GOT, I was simply allowing GCC to compile my test 118 | program and place its ELF object data to their default location. This 119 | presented several problems. For one, calling mprotect() on non 120 | page-aligned locations was failing. It was simply not feasible to 121 | mprotect() the ELF base and beyond just to protect the GOT. 122 | There are plenty of other memory segments adjacent to the GOT that 123 | MUST be writable such as the .bss segment. So the first problem 124 | becomes quite clear. If we want to mprotect() the GOT, or any 125 | other sensitive memory segments, then we have to group them near a 126 | page aligned boundary separate from other segments that must remain 127 | writable. Of course like anything else, there is the easy way to do 128 | this and the hard way. The hard way consists of rewriting the binary, 129 | which is necessary for closed source applications, this has the 130 | potential to be messy which means we may interfere with our 131 | application. The easy way is to use a GNU linker script to tell the 132 | linker how to setup our sections at compile time. This is of course 133 | the preferred method.

The GNU linker (ld) has its own 134 | interpreted scripting language. This language is used to write linker 135 | files that tell the linker how to create the final object file. To 136 | view the default linker script type the following: 137 |

138 |
# ld --verbose
139 | GNU ld (GNU Binutils for Ubuntu) 2.18.0.20080103
140 |   Supported emulations:
141 |    elf_i386
142 |    i386linux
143 |    elf_x86_64
144 | using internal linker script:
145 | ==================================================
146 | /* Script for -z combreloc: combine and sort reloc sections */
147 | OUTPUT_FORMAT("elf32-i386", "elf32-i386",
148 |               "elf32-i386")
149 | OUTPUT_ARCH(i386)
150 | ...(output cropped)...

151 | For additional linker scripts, most Linux distributions store them in 152 | /usr/lib/ldscripts. The default script is around 197 lines 153 | long. Most open source projects use the default linker script, as it 154 | generates a binary with a common layout. In order to make our self 155 | GOT protection technique work we are going to have to create a custom 156 | linker script. One which places sensitive areas of memory where we 157 | want them to be. Preferably on page aligned boundaries so that 158 | mprotect() can be used.

Let's take a quick look at the 159 | typical layout of ELF object data in an example program: 160 |

161 |
# cat test-prog.c
162 | #include < stdio.h>
163 | #include < stdlib.h>
164 | #include < string.h>
165 | 
166 | char global_var[256];
167 | 
168 | int main(int argc, char *argv[])
169 | {
170 |     char *ptr = NULL;
171 |     char buffer[128];
172 | 
173 |     ptr = buffer;
174 | 
175 |     if(argc == 3)
176 |     {
177 |         strcpy(ptr, argv[1]);   
178 |         printf("Copied buffer: [%s]\n", buffer);
179 |         strcpy(ptr, argv[1]);
180 | 
181 |         strncpy(global_var, argv[2], 255);  
182 |         printf("Copied into global_var: [%s]\n", global_var);
183 |     }
184 | 
185 | return 0;
186 | }
187 | 
188 | # gcc -o test-prog test-prog.c
189 | # readelf -Sl test-prog
190 | There are 36 section headers, starting at offset 0xe64:
191 | 
192 | Section Headers:
193 |   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
194 |   [ 0]                   NULL            00000000 000000 000000 00      0   0  0
195 |   [ 1] .interp           PROGBITS        08048114 000114 000013 00   A  0   0  1
196 |   [ 2] .note.ABI-tag     NOTE            08048128 000128 000020 00   A  0   0  4
197 |   [ 3] .hash             HASH            08048148 000148 000034 04   A  5   0  4
198 |   [ 4] .gnu.hash         GNU_HASH        0804817c 00017c 000020 04   A  5   0  4
199 |   [ 5] .dynsym           DYNSYM          0804819c 00019c 000080 10   A  6   1  4
200 |   [ 6] .dynstr           STRTAB          0804821c 00021c 000076 00   A  0   0  1
201 |   [ 7] .gnu.version      VERSYM          08048292 000292 000010 02   A  5   0  2
202 |   [ 8] .gnu.version_r    VERNEED         080482a4 0002a4 000030 00   A  6   1  4
203 |   [ 9] .rel.dyn          REL             080482d4 0002d4 000008 08   A  5   0  4
204 |   [10] .rel.plt          REL             080482dc 0002dc 000030 08   A  5  12  4
205 |   [11] .init             PROGBITS        0804830c 00030c 000030 00  AX  0   0  4
206 |   [12] .plt              PROGBITS        0804833c 00033c 000070 04  AX  0   0  4
207 |   [13] .text             PROGBITS        080483b0 0003b0 00022c 00  AX  0   0 16
208 |   [14] .fini             PROGBITS        080485dc 0005dc 00001c 00  AX  0   0  4
209 |   [15] .rodata           PROGBITS        080485f8 0005f8 00003b 00   A  0   0  4
210 |   [16] .eh_frame         PROGBITS        08048634 000634 000004 00   A  0   0  4
211 |   [17] .ctors            PROGBITS        08049638 000638 000008 00  WA  0   0  4
212 |   [18] .dtors            PROGBITS        08049640 000640 000008 00  WA  0   0  4
213 |   [19] .jcr              PROGBITS        08049648 000648 000004 00  WA  0   0  4
214 |   [20] .dynamic          DYNAMIC         0804964c 00064c 0000d0 08  WA  6   0  4
215 |   [21] .got              PROGBITS        0804971c 00071c 000004 04  WA  0   0  4
216 |   [22] .got.plt          PROGBITS        08049720 000720 000024 04  WA  0   0  4
217 |   [23] .data             PROGBITS        08049744 000744 00000c 00  WA  0   0  4
218 |   [24] .bss              NOBITS          08049760 000750 000120 00  WA  0   0 32
219 |   [25] .comment          PROGBITS        00000000 000750 000126 00      0   0  1
220 |   [26] .debug_aranges    PROGBITS        00000000 000878 000050 00      0   0  8
221 |   [27] .debug_pubnames   PROGBITS        00000000 0008c8 000025 00      0   0  1
222 |   [28] .debug_info       PROGBITS        00000000 0008ed 0001a7 00      0   0  1
223 |   [29] .debug_abbrev     PROGBITS        00000000 000a94 00006f 00      0   0  1
224 |   [30] .debug_line       PROGBITS        00000000 000b03 000129 00      0   0  1
225 |   [31] .debug_str        PROGBITS        00000000 000c2c 0000bb 01  MS  0   0  1
226 |   [32] .debug_ranges     PROGBITS        00000000 000ce8 000040 00      0   0  8
227 |   [33] .shstrtab         STRTAB          00000000 000d28 000139 00      0   0  1
228 |   [34] .symtab           SYMTAB          00000000 001404 0004e0 10     35  55  4
229 |   [35] .strtab           STRTAB          00000000 0018e4 000250 00      0   0  1
230 | Key to Flags:
231 |   W (write), A (alloc), X (execute), M (merge), S (strings)
232 |   I (info), L (link order), G (group), x (unknown)
233 |   O (extra OS processing required) o (OS specific), p (processor specific)
234 | 
235 | Elf file type is EXEC (Executable file)
236 | Entry point 0x80483b0
237 | There are 7 program headers, starting at offset 52
238 | 
239 | Program Headers:
240 |   Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
241 |   PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
242 |   INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
243 |       [Requesting program interpreter: /lib/ld-linux.so.2]
244 |   LOAD           0x000000 0x08048000 0x08048000 0x00638 0x00638 R E 0x1000
245 |   LOAD           0x000638 0x08049638 0x08049638 0x00118 0x00248 RW  0x1000
246 |   DYNAMIC        0x00064c 0x0804964c 0x0804964c 0x000d0 0x000d0 RW  0x4
247 |   NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
248 |   GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
249 | 
250 |  Section to Segment mapping:
251 |   Segment Sections...
252 |    00     
253 |    01     .interp 
254 |    02     .interp .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame 
255 |    03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 
256 |    04     .dynamic 
257 |    05     .note.ABI-tag 
258 |    06     

259 | The section header has its own permission markings such as W for 260 | write, A for allocate, X for execute and more. However these are 261 | inconsequential to the program once its in memory. The ELF program 262 | header and its correlation to the section header is what we need to 263 | look at. The process of our protection technique is to make sure the 264 | dynamic linker has resolved all function locations at process 265 | creation and then mark the GOT read only. As stated before this is 266 | not possible yet because the GOT (.got.plt in our example) appears to 267 | be on the same page as the .bss and other important sections that 268 | must be writable. So we must use our linker script to control where 269 | the GOT is placed. Use the following command to pipe the ld default 270 | script to a text file. 271 |

272 |
# ld --verbose > ld.txt

273 | Now we want to edit this file. Make sure to strip all contents above 274 | the '===' dashed line, these are just comments. You also want to 275 | remove the trailing '===' line from the file. Now around line 136 you 276 | should see the '.got' and '.got.plt' sections. We want to change a 277 | few lines here that tells the GNU linker where to place the sections. 278 | The following patch accomplishes this by telling the linker to place 279 | the .got and .got.plt at least one MAXPAGESIZE beyond the .bss 280 | section. 281 |

282 |
--- ../testing/ld.txt   2008-05-02 10:20:21.000000000 -0400
283 | +++ ../testing/new.txt  2008-05-02 14:26:29.000000000 -0400
284 | @@ -133,9 +133,6 @@
285 |    .jcr            : { KEEP (*(.jcr)) }
286 |    .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
287 |    .dynamic        : { *(.dynamic) }
288 | -  .got            : { *(.got) }
289 | -  . = DATA_SEGMENT_RELRO_END (12, .);
290 | -  .got.plt        : { *(.got.plt) }
291 |    .data           :
292 |    {
293 |      *(.data .data.* .gnu.linkonce.d.*)
294 | @@ -159,6 +156,9 @@
295 |    }
296 |    . = ALIGN(32 / 8);
297 |    . = ALIGN(32 / 8);
298 | +  .got ALIGN(CONSTANT(MAXPAGESIZE))           : { *(.got) }    /* SPG Align the .got at least one page beyond .bss */
299 | +  . = DATA_SEGMENT_RELRO_END (12, .);
300 | +  .got.plt        : { *(.got.plt) }
301 |    _end = .; PROVIDE (end = .);
302 |    . = DATA_SEGMENT_END (.);
303 |    /* Stabs debugging sections.  */

304 | Notice the extra 'ALIGN(CONSTANT(MAXPAGESIZE))' expression, this pads 305 | our sections at least one page beyond the end of the .bss section. 306 | Now we compile, make sure the sections are placed correctly and test 307 | the program. You can tell the GNU linker to use a different linker 308 | script by using the gcc command line flags '-Wl,-T new.txt' where 309 | new.txt is your modified linker script.

310 |
# gcc -o test-prog test-prog.c -DSPG_DEBUG -Wl,-T new.txt 
311 | # readelf -lS test-prog
312 | There are 36 section headers, starting at offset 0x273c:
313 | 
314 | Section Headers:
315 |   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
316 |   [ 0]                   NULL            00000000 000000 000000 00      0   0  0
317 |   [ 1] .interp           PROGBITS        08048134 000134 000013 00   A  0   0  1
318 |   [ 2] .note.ABI-tag     NOTE            08048148 000148 000020 00   A  0   0  4
319 |   [ 3] .hash             HASH            08048168 000168 000034 04   A  5   0  4
320 |   [ 4] .gnu.hash         GNU_HASH        0804819c 00019c 000020 04   A  5   0  4
321 |   [ 5] .dynsym           DYNSYM          080481bc 0001bc 000080 10   A  6   1  4
322 |   [ 6] .dynstr           STRTAB          0804823c 00023c 000076 00   A  0   0  1
323 |   [ 7] .gnu.version      VERSYM          080482b2 0002b2 000010 02   A  5   0  2
324 |   [ 8] .gnu.version_r    VERNEED         080482c4 0002c4 000030 00   A  6   1  4
325 |   [ 9] .rel.dyn          REL             080482f4 0002f4 000008 08   A  5   0  4
326 |   [10] .rel.plt          REL             080482fc 0002fc 000030 08   A  5  12  4
327 |   [11] .init             PROGBITS        0804832c 00032c 000030 00  AX  0   0  4
328 |   [12] .plt              PROGBITS        0804835c 00035c 000070 04  AX  0   0  4
329 |   [13] .text             PROGBITS        080483d0 0003d0 00022c 00  AX  0   0 16
330 |   [14] .fini             PROGBITS        080485fc 0005fc 00001c 00  AX  0   0  4
331 |   [15] .rodata           PROGBITS        08048618 000618 00003b 00   A  0   0  4
332 |   [16] .eh_frame         PROGBITS        08048654 000654 000004 00   A  0   0  4
333 |   [17] .ctors            PROGBITS        08049000 001000 000008 00  WA  0   0  4
334 |   [18] .dtors            PROGBITS        08049008 001008 000008 00  WA  0   0  4
335 |   [19] .jcr              PROGBITS        08049010 001010 000004 00  WA  0   0  4
336 |   [20] .dynamic          DYNAMIC         08049014 001014 0000d0 08  WA  6   0  4
337 |   [21] .data             PROGBITS        080490e4 0010e4 00000c 00  WA  0   0  4
338 |   [22] .bss              NOBITS          08049100 0010f0 000120 00  WA  0   0 32
339 |   [23] .got              PROGBITS        0804a000 002000 000004 04  WA  0   0  4
340 |   [24] .got.plt          PROGBITS        0804a004 002004 000024 04  WA  0   0  4
341 |   [25] .comment          PROGBITS        00000000 002028 000126 00      0   0  1
342 |   [26] .debug_aranges    PROGBITS        00000000 002150 000050 00      0   0  8
343 |   [27] .debug_pubnames   PROGBITS        00000000 0021a0 000025 00      0   0  1
344 |   [28] .debug_info       PROGBITS        00000000 0021c5 0001a7 00      0   0  1
345 |   [29] .debug_abbrev     PROGBITS        00000000 00236c 00006f 00      0   0  1
346 |   [30] .debug_line       PROGBITS        00000000 0023db 000129 00      0   0  1
347 |   [31] .debug_str        PROGBITS        00000000 002504 0000bb 01  MS  0   0  1
348 |   [32] .debug_ranges     PROGBITS        00000000 0025c0 000040 00      0   0  8
349 |   [33] .shstrtab         STRTAB          00000000 002600 000139 00      0   0  1
350 |   [34] .symtab           SYMTAB          00000000 002cdc 0004e0 10     35  55  4
351 |   [35] .strtab           STRTAB          00000000 0031bc 000258 00      0   0  1
352 | Key to Flags:
353 |   W (write), A (alloc), X (execute), M (merge), S (strings)
354 |   I (info), L (link order), G (group), x (unknown)
355 |   O (extra OS processing required) o (OS specific), p (processor specific)
356 | 
357 | Elf file type is EXEC (Executable file)
358 | Entry point 0x80483d0
359 | There are 8 program headers, starting at offset 52
360 | 
361 | Program Headers:
362 |   Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
363 |   PHDR           0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4
364 |   INTERP         0x000134 0x08048134 0x08048134 0x00013 0x00013 R   0x1
365 |       [Requesting program interpreter: /lib/ld-linux.so.2]
366 |   LOAD           0x000000 0x08048000 0x08048000 0x00658 0x00658 R E 0x1000
367 |   LOAD           0x001000 0x08049000 0x08049000 0x000f0 0x00220 RW  0x1000
368 |   LOAD           0x002000 0x0804a000 0x0804a000 0x00028 0x00028 RW  0x1000
369 |   DYNAMIC        0x001014 0x08049014 0x08049014 0x000d0 0x000d0 RW  0x4
370 |   NOTE           0x000148 0x08048148 0x08048148 0x00020 0x00020 R   0x4
371 |   GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
372 | 
373 |  Section to Segment mapping:
374 |   Segment Sections...
375 |    00     
376 |    01     .interp 
377 |    02     .interp .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame 
378 |    03     .ctors .dtors .jcr .dynamic .data .bss 
379 |    04     .got .got.plt 
380 |    05     .dynamic 
381 |    06     .note.ABI-tag 
382 |    07    
383 |  
384 | # ./test-prog Hello World
385 | Copied buffer: [Hello]
386 | Copied into global_var: [World]

387 | Notice the .got and .got.plt sections are placed appropriately. 388 | However we still have a problem here, they are still marked RW. This 389 | is where we need to get creative with our constructor function. Our 390 | constructor function has to be able to locate its own GOT in memory 391 | and mprotect() it read-only. The simplest way is by locating 392 | its own ELF header in memory, locating the program header for the 393 | PT_DYNAMIC segment and using it to find the dynamic DT_PLTGOT entry. 394 | This entry gives us our .got.plt location. Using a simple (but 395 | hackish) AND-bitmask we can grab the address the page the .got.plt 396 | starts on and mprotect() it accordingly. My constructor 397 | function plus the example code is below and commented in detail. 398 |

399 |
# cat got-spg.c
400 | #include < stdio.h>
401 | #include < stdlib.h>
402 | #include < string.h>
403 | #include < unistd.h>
404 | #include < elf.h>
405 | #include < errno.h>
406 | #include < sys/mman.h>
407 | 
408 | /* gcc -o got-spg got-spg.c -DSPG_DEBUG -Wl,-T new.txt */
409 | 
410 | #define ERR  -1
411 | 
412 | __attribute__((constructor)) void __self_protect_got()
413 | {
414 | 
415 | int i;
416 | int ret;
417 | int loc = 0;
418 | 
419 | #ifdef SPG_DEBUG
420 |         fprintf(stdout, "\nSelf Protecting GOT\n\n");
421 | #endif
422 | 
423 | /* Check if ld has the right env settings - RTLD_LAZY means we cant touch the GOT */
424 | char *lazy = getenv("LD_BIND_NOW");
425 | 
426 |         if(lazy == NULL)
427 |         {
428 |                 #ifdef SPG_DEBUG
429 |                         fprintf(stdout, "\nSkipping self protection...\nPlease set LD_BIND_NOW=1 and retry\n\n");
430 |                 #endif
431 | 
432 |                 return;
433 |         }
434 | 
435 | /* Get the page size, 4096 on x86 */
436 | int     pagesize = sysconf(_SC_PAGE_SIZE);
437 | 
438 |         if(pagesize == ERR)
439 |         {
440 |                 #ifdef SPG_DEBUG
441 |                         fprintf(stdout, "sysconf() failed, assuming 4096 byte page size (x86 default)\n");
442 |                 #endif
443 | 
444 |                 pagesize = 4096;
445 |         }
446 |         else
447 |         {
448 |                 #ifdef SPG_DEBUG
449 |                         fprintf(stdout, "Pagesize = %d\n", pagesize);
450 |                 #endif
451 |         }
452 | 
453 | /* Locate the ELF base address. This is the only hard coded address in the whole function */
454 | char *base_ptr = (char *) 0x8048000;
455 | 
456 |     /* Randomizing the ELF base address means we may have to search for it */
457 |     while(*base_ptr != 'E' && *base_ptr+1 != 'L' && *base_ptr+2 != 'F')
458 |     {
459 |         *base_ptr++;
460 |     }
461 | 
462 |     *base_ptr--;
463 | 
464 | Elf32_Ehdr *elf_header = (Elf32_Ehdr *) base_ptr; /* Elf Header */
465 | Elf32_Phdr *phdr;               /* ELF program header */
466 | Elf32_Phdr *dyn_phdr = NULL;    /* Program header marked 'dynamic' */
467 | Elf32_Dyn *dyn;                 /* Specific dynamic segment entry for .got.plt */
468 | 
469 |         /* Iterate the program header for the dynamic segment */
470 |         for(i=0;i < elf_header->e_phnum; i++)
471 |         {
472 |                 phdr = (Elf32_Phdr *)(base_ptr + elf_header->e_phoff + (i * elf_header->e_phentsize));
473 | 
474 |                 if(phdr->p_type == PT_DYNAMIC)
475 |                 {
476 |                         dyn_phdr = phdr;
477 |                         break;
478 |                 }
479 |         }
480 | 
481 |         if(dyn_phdr == NULL)
482 |         {
483 |                 #ifdef SPG_DEBUG
484 |                         fprintf(stdout, "Could not locate the dynamic segment\n\n");
485 |                 #endif
486 | 
487 |                 return;
488 |         }
489 | 
490 |         /* Iterate through the dynamic segment for an entry of type DT_PLTGOT */
491 |         for(i=0;i < dyn_phdr->p_filesz / sizeof(Elf32_Dyn); i++)
492 |         {
493 |                 dyn = (Elf32_Dyn *)(base_ptr + dyn_phdr->p_offset + (i * sizeof(Elf32_Dyn)));
494 | 
495 |                 /* Once its found, save the location of our .got.plt */
496 |                 if(dyn->d_tag == DT_PLTGOT)
497 |                 {
498 |                         #ifdef SPG_DEBUG
499 |                                 fprintf(stdout, "GOTPLT Located @ %x\n", dyn->d_un.d_val);
500 |                         #endif
501 | 
502 |                         loc = dyn->d_un.d_val;
503 |                         break;
504 |                 }
505 |         }
506 | 
507 |         /* We can't do anything without an address for the GOT */
508 |         if(loc == 0)
509 |         {
510 |                 #ifdef SPG_DEBUG
511 |                         fprintf(stdout, "GOTPLT not located\n\n");
512 |                 #endif
513 | 
514 |                 return;
515 |         }
516 | 
517 |         /* AND-bitmask would turn 0x804b004 into 0x804b000, which is our page aligned addr */
518 |         loc = (loc & 0xffff000);
519 | 
520 |         #ifdef SPG_DEBUG
521 |                 fprintf(stdout, "Protecting page found at %x\n", loc);
522 |         #endif
523 | 
524 |         /* Set the entire page READ-ONLY */
525 |         ret = mprotect((void *) loc, pagesize, PROT_READ);
526 | 
527 |         if(ret != ERR)
528 |         {
529 |                 #ifdef SPG_DEBUG
530 |                         fprintf(stdout, "GOT segment now protected for process [%d]...\n\n", getpid());
531 |                 #endif
532 |         }
533 |         else
534 |         {
535 |                 #ifdef SPG_DEBUG
536 |                         perror("mprotect");
537 |                         fprintf(stdout, "\n\n");
538 |                 #endif
539 |         }
540 | 
541 | return;
542 | }
543 | 
544 | /* Main Program */
545 | 
546 | char global_var[256];
547 | 
548 | int main(int argc, char *argv[])
549 | {
550 |         char *ptr;
551 |         char buffer[128];
552 | 
553 |         ptr = buffer;
554 | 
555 |     if(argc == 3)
556 |     {
557 |         strcpy(ptr, argv[1]);   /* A segfault would happen here if strcpy
558 |                                 was not already present in the GOT */
559 |         printf("Copied buffer: [%s]\n", buffer);
560 |         strcpy(ptr, argv[1]);
561 | 
562 |         strncpy(global_var, argv[2], 255);  /* Simply to test global data is still OK */
563 |         printf("Copied into global_var: [%s]\n", global_var);
564 |     }
565 | 
566 |         /* This is so we can grab the PID and print /proc/(pid)/maps to check if it worked */
567 |         sleep(35);
568 | 
569 | return 0;
570 | }
571 | 
572 | # gcc -o got-spg got-spg.c -DSPG_DEBUG -Wl,-T new.txt 
573 | # ./got-spg Hello World
574 | 
575 | Self Protecting GOT
576 | Pagesize = 4096
577 | GOTPLT Located @ 804a004
578 | Protecting page found at 804a000
579 | GOT segment now protected for process [16722]...
580 | 
581 | Copied buffer: [Hello]
582 | Copied into global_var: [World]
583 | 
584 | ...(in another terminal)...
585 | 
586 | # cat /proc/16722/maps
587 | 08048000-08049000 r-xp 00000000 07:00 147464     /testing/got-spg
588 | 08049000-0804a000 rw-p 00001000 07:00 147464     /testing/SPG/got-spg
589 | 0804a000-0804b000 r--p 00002000 07:00 147464     /testing/SPG/got-spg
590 | b7e0f000-b7e10000 rw-p b7e0f000 00:00 0 
591 | b7e10000-b7f59000 r-xp 00000000 08:02 6214306    /lib/tls/i686/cmov/libc-2.7.so
592 | b7f59000-b7f5a000 r--p 00149000 08:02 6214306    /lib/tls/i686/cmov/libc-2.7.so
593 | b7f5a000-b7f5c000 rw-p 0014a000 08:02 6214306    /lib/tls/i686/cmov/libc-2.7.so
594 | b7f5c000-b7f5f000 rw-p b7f5c000 00:00 0 
595 | b7f73000-b7f76000 rw-p b7f73000 00:00 0 
596 | b7f76000-b7f77000 r-xp b7f76000 00:00 0          [vdso]
597 | b7f77000-b7f91000 r-xp 00000000 08:02 6213883    /lib/ld-2.7.so
598 | b7f91000-b7f93000 rw-p 00019000 08:02 6213883    /lib/ld-2.7.so
599 | bf961000-bf976000 rw-p bffeb000 00:00 0          [stack]

600 | Our process map above shows the page we marked read-only is being 601 | enforced properly. If an attacker tried to write a pointer value to 602 | the GOT during an exploitation attempt it would fail.

Other 603 | Protections

So now we have protected our own .got.plt 604 | from being written to by attackers. But what other parts of memory 605 | can we protect? The .dtors section seems like a good target. And all 606 | we need to do is move its location in the linker script below .got 607 | and .got.plt. What about .rodata? .jcr? ctors? data.rel.ro? Below is 608 | a patch to the default linker script to move these sections.

609 |
--- ld.txt      2008-05-02 10:20:21.000000000 -0400
610 | +++ new.txt     2008-05-03 00:57:52.000000000 -0400
611 | @@ -101,6 +101,33 @@
612 |      KEEP (*(SORT(.fini_array.*)))
613 |      PROVIDE_HIDDEN (__fini_array_end = .);
614 |    }
615 | +  .dynamic        : { *(.dynamic) }
616 | +  .data           :
617 | +  {
618 | +    *(.data .data.* .gnu.linkonce.d.*)
619 | +    KEEP (*(.gnu.linkonce.d.*personality*))
620 | +    SORT(CONSTRUCTORS)
621 | +  }
622 | +  .data1          : { *(.data1) }
623 | +  _edata = .; PROVIDE (edata = .);
624 | +  __bss_start = .;
625 | +  .bss            :
626 | +  {
627 | +   *(.dynbss)
628 | +   *(.bss .bss.* .gnu.linkonce.b.*)
629 | +   *(COMMON)
630 | +   /* Align here to ensure that the .bss section occupies space up to
631 | +      _end.  Align after .bss to ensure correct alignment even if the
632 | +      .bss section disappears because there are no input sections.
633 | +      FIXME: Why do we need it? When there is no .bss section, we don't
634 | +      pad the .data section.  */
635 | +   . = ALIGN(. != 0 ? 32 / 8 : 1);
636 | +  }
637 | +  . = ALIGN(32 / 8);
638 | +  . = ALIGN(32 / 8);
639 | +  .got ALIGN(CONSTANT(MAXPAGESIZE))           : { *(.got) }
640 | +  . = DATA_SEGMENT_RELRO_END (12, .);
641 | +  .got.plt        : { *(.got.plt) }
642 |    .ctors          :
643 |    {
644 |      /* gcc uses crtbegin.o to find the start of
645 | @@ -132,33 +159,6 @@
646 |    }
647 |    .jcr            : { KEEP (*(.jcr)) }
648 |    .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
649 | -  .dynamic        : { *(.dynamic) }
650 | -  .got            : { *(.got) }
651 | -  . = DATA_SEGMENT_RELRO_END (12, .);
652 | -  .got.plt        : { *(.got.plt) }
653 | -  .data           :
654 | -  {
655 | -    *(.data .data.* .gnu.linkonce.d.*)
656 | -    KEEP (*(.gnu.linkonce.d.*personality*))
657 | -    SORT(CONSTRUCTORS)
658 | -  }
659 | -  .data1          : { *(.data1) }
660 | -  _edata = .; PROVIDE (edata = .);
661 | -  __bss_start = .;
662 | -  .bss            :
663 | -  {
664 | -   *(.dynbss)
665 | -   *(.bss .bss.* .gnu.linkonce.b.*)
666 | -   *(COMMON)
667 | -   /* Align here to ensure that the .bss section occupies space up to
668 | -      _end.  Align after .bss to ensure correct alignment even if the
669 | -      .bss section disappears because there are no input sections.
670 | -      FIXME: Why do we need it? When there is no .bss section, we don't
671 | -      pad the .data section.  */
672 | -   . = ALIGN(. != 0 ? 32 / 8 : 1);
673 | -  }
674 | -  . = ALIGN(32 / 8);
675 | -  . = ALIGN(32 / 8);
676 |    _end = .; PROVIDE (end = .);
677 |    . = DATA_SEGMENT_END (.);
678 |    /* Stabs debugging sections.  */

679 |
Conclusion

We can easily turn our constructor 680 | function into a simple library that can be linked into any program. I 681 | tested the GOT protection technique on Snort IDS version 2.8.1. Below 682 | is the output of its ELF section header and its memory mapping.

683 |
# readelf -lS /usr/src/snort-2.8.1/src/snort
684 | ...
685 |   [17] .dynamic          DYNAMIC         080e9000 0a1000 0000f8 08  WA  6   0  4
686 |   [18] .data             PROGBITS        080e9100 0a1100 582668 00  WA  0   0 32
687 |   [19] .bss              NOBITS          0866b780 623768 5c68bc 00  WA  0   0 32
688 |   [20] .got              PROGBITS        08c33000 624000 000004 04  WA  0   0  4
689 |   [21] .got.plt          PROGBITS        08c33004 624004 000280 04  WA  0   0  4
690 |   [22] .ctors            PROGBITS        08c33284 624284 00000c 00  WA  0   0  4
691 |   [23] .dtors            PROGBITS        08c33290 624290 000008 00  WA  0   0  4
692 |   [24] .jcr              PROGBITS        08c33298 624298 000004 00  WA  0   0  4
693 | ...
694 | # cat /proc/`pidof snort`/maps
695 | 08048000-080e9000 r-xp 00000000 08:02 4891501    /usr/src/snort-2.8.1/src/snort
696 | 080e9000-0866c000 rw-p 000a1000 08:02 4891501    /usr/src/snort-2.8.1/src/snort
697 | 0866c000-08c33000 rw-p 0866c000 00:00 0 
698 | 08c33000-08c34000 r--p 00624000 08:02 4891501    /usr/src/snort-2.8.1/src/snort
699 | 08c34000-08df5000 rw-p 08c34000 00:00 0          [heap]

700 | When run with LD_BIND_NOW environment variable and our constructor 701 | function, Snort worked without any runtime issues. The interesting 702 | part about the technique is that it was done without any special 703 | dynamic linker support and does not use or depend on its '-z relro' 704 | option. Therefore the concept is technically 'portable' to another 705 | ELF based system that uses a different tool set without native 706 | protection mechanisms.

References

Fedora 707 | Security Enhancements 708 | http://people.redhat.com/drepper/nonselsec.pdf
GCC 709 | Attributes 710 | http://www.ohse.de/uwe/articles/gcc-attributes.html
Dynamic 711 | Linker Scripts http://sourceware.org/binutils/docs-2.18/ld/
RELRO 712 | Binutils http://sources.redhat.com/ml/binutils/2004-01/msg00070.html

713 |



http://em386.blogspot.com
Struct 714 | Software 715 |

716 | --------------------------------------------------------------------------------