pwn.college: Building a Web Server

来来来,你告诉我,什么TMD叫Web,什么TMD叫Asm。这TM八岁 ?_?

Building a Web Server

level1

正常的exit汇编

1
2
3
4
5
6
7
8
9
.intel_syntax noprefix
.global _start
.section .text
_start:
mov rdi,0
mov rax,60
syscall

.section .data

level2

要实现socket,其系统调用为41。原型int socket(int domain, int type, int protocol);

socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3

也就是说,domain为AF_INET,type为SOCK_STREAM,protocol为IPPROTO_IP

其中,关于第一个参数说明如下:

  • AF_INET (2): IPv4 地址族。
  • AF_INET6 (10): IPv6 地址族。
  • AF_UNIX (1): Unix 套接字。 这些套接字用于进程间通信,通常在本地主机上使用。 它们不是基于网络的。
  • AF_BLUETOOTH (12): 用于蓝牙通信的套接字类型。
  • AF_PACKET (17): 允许应用程序访问底层网络帧。
  • AF_NETLINK (16): 用于内核模块和用户空间进程之间的消息传递。
  • AF_AX25 (11): 用于 AX.25 协议的套接字类型。
  • AF_X25 (15): 用于 X.25 协议的套接字类型。
  • AF_ATMPVC (21): 用于 ATM (Asynchronous Transfer Mode) 协议的虚拟通道 (PVC)。
  • AF_APPLETALK (23): 用于 AppleTalk 协议的套接字类型。
  • AF_NETBIOS (24): 用于 NetBIOS 协议的套接字类型。

关于第二个参数说明如下:

  • SOCK_STREAM (1): 面向连接的流式套接字。数据被可靠地传输,以字节流的形式发送,顺序保证。 这类似于 TCP。
  • SOCK_DGRAM (2): 无连接的数据报套接字。数据包可能会丢失或顺序颠倒。 这类似于 UDP。 数据被发送成独立的数据报,没有可靠性保证。
  • SOCK_SEQPACKET (3): 面向连接的序列包套接字。它试图提供 TCP 的可靠性但不需要三次握手,在某些情况下性能更好。 这种类型比较少见。
  • SOCK_RAW (3): 原始套接字。允许应用程序访问底层网络协议。程序员可以访问网络协议的详细信息,控制传输的整个过程。这需要对网络协议有深入的了解,使用风险比较高。

关于第三个参数说明如下:

  • IPPROTO_IP (0): IP 协议。 这个值通常在使用 TCP 或 UDP 时使用,因为它在底层协议栈中就已经存在了。 它不是一个独立的协议,而是 IP 协议栈的一部分。
  • IPPROTO_TCP (6): 传输控制协议 (TCP)。 这指定了使用 TCP 协议。
  • IPPROTO_UDP (17): 用户数据报协议 (UDP)。 这指定了使用 UDP 协议。
  • IPPROTO_ICMP (1): 因特网控制消息协议 (ICMP)。 ICMP 用于错误报告和查询。
  • IPPROTO_IGMP (2): 因特网组管理协议 (IGMP)。 IGMP 用于多播。
  • IPPROTO_IPIP (4): IP over IP。 这允许一个 IP 数据报封装在另一个 IP 数据报中。
  • IPPROTO_RSVP (46): 资源预留协议 (RSVP)。 用于保证多媒体应用程序的带宽。
  • IPPROTO_GRE (47): 通用路由封装 (GRE)。 允许将不同协议的数据包封装在 IP 数据报中。
  • IPPROTO_AH (51): 认证头 (AH)。 用于安全通信。

那么最终的exp为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.intel_syntax noprefix
.global _start
.section .text
_start:
mov rax, 41 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rdi, 2 # AF_INET(2)
mov rsi, 1 # SOCK_STREAM(1)
mov rdx, 0 # IPPROTO_IP(0)
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data

level3

实现bind,bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0

bind原型:int bind(int sockfd, const struct sockaddr addr, socklen_t addrlen)

好吧,我想不出来。看别人的wp,很好,用到了.data段定义这个结构体。然后端口是0x5000,是80的小端序

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
.intel_syntax noprefix
.global _start
.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0

绑定的地址为0.0.0.0即bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0

level4

要完成listen(3, 0) = 0,这个简单啊,listen系统调用号为50

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
.intel_syntax noprefix
.global _start
.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0

level5

要完成accept(3, NULL, NULL) = 4 accept的系统调用号为43

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
.intel_syntax noprefix
.global _start
.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0

level6

这一关有点东西,要完成三个东西:read(4, <read_request>, <read_request_count>) = <read_request_result>write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19close(4) = 0

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
.intel_syntax noprefix
.global _start
.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rdi, 4
mov rsi, rsp
mov rdx, 256
mov rax, 0
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi, 4 # close(4) = 0
mov rax, 3
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"

这一关的目的是,能够接收客户端发送来的消息,并反馈200状态码。

level7

那么这一关,就响应客户端的请求,将它要的文件,发送给它。那么就有个问题了,第一个read会读取客户端发来的请求,然后这个请求很长,该怎么精准拿到这个文件呢?

1
read(4, "GET /tmp/tmpper_iarh HTTP/1.1\r\nHost: localhost\r\nUser-Agent: python-requests/2.32.3\r\nAccept-Encoding: gzip, deflate, zstd\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n", 256) = 161

我发现了一个简单的方法用变量存起来就好了,然后截断第一个空格。

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
.intel_syntax noprefix
.global _start


.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rdi, 4 # read(4,buf,count_bytes)
lea rsi, buffer
mov rdx, 256
mov rax, 0
syscall

lea rsi, buffer+4
lea rdi, file_path
loop_start:
mov al, byte ptr [rsi]
cmp al, ' '
je get_file_path
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start

get_file_path:
lea rdi, file_path # open
mov rsi, 0
mov rax, 2
syscall

mov rdi, 5 # read
mov rsi, rsp
mov rdx, 256
mov rax, 0
syscall
mov r8, rax

mov rax, 0
mov rbx, 0

mov rdi, 5
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi, 4
mov rsi, rsp
mov rdx, r8
mov rax, 1
syscall

mov rdi, 4 # close(4) = 0
mov rax, 3
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"
buffer:
.space 256
file_path:
.space 64

level8

这一关就是加一个accept调用即可。

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
.intel_syntax noprefix
.global _start


.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rdi, 4 # read(4,buf,count_bytes)
lea rsi, buffer
mov rdx, 256
mov rax, 0
syscall

lea rsi, buffer+4
lea rdi, file_path
loop_start:
mov al, byte ptr [rsi]
cmp al, ' '
je get_file_path
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start

get_file_path:
lea rdi, file_path # open("file_path", O_RDONLY)
mov rsi, 0
mov rax, 2
syscall

mov rdi, 5 # read(the_file,buf,256)
mov rsi, rsp
mov rdx, 256
mov rax, 0
syscall
mov r8, rax

mov rdi, 5 # close(5) = 0
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi, 4 # write(4, "file_buf", r8)
mov rsi, rsp
mov rdx, r8
mov rax, 1
syscall

mov rdi, 4 # close(4) = 0
mov rax, 3
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"
buffer:
.space 256
file_path:
.space 64

level9

这道题要用fork,经过第七题的拷打之后,简单一些了。

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
.intel_syntax noprefix
.global _start


.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rax, 57 # fork()
syscall

cmp rax, 0
je child_process
mov rdi, 4 # close(4)
mov rax, 3
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall
jmp done

child_process:
mov rdi, 3 # close(3)
mov rax, 3
syscall
mov rdi, 4 # read(4,buf,count_bytes)
lea rsi, buffer
mov rdx, 256
mov rax, 0
syscall

lea rsi, buffer+4
lea rdi, file_path
loop_start:
mov al, byte ptr [rsi]
cmp al, ' '
je get_file_path
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start

get_file_path:
lea rdi, file_path # open("file_path", O_RDONLY)
mov rsi, 0
mov rax, 2
syscall

mov rdi, 3 # read(the_file,buf,256)
mov rsi, rsp
mov rdx, 256
mov rax, 0
syscall
mov r8, rax

mov rdi, 3 # close(5) = 0
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi, 4 # write(4, "file_buf", r8)
mov rsi, rsp
mov rdx, r8
mov rax, 1
syscall

mov rdi, 4 # close(4) = 0
mov rax, 3
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

done:

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"
buffer:
.space 256
file_path:
.space 64

level10

这一关,还挺折磨的。对于客户端发来的信息:

1
"POST /tmp/tmpgjb407yq HTTP/1.1\r\nHost: localhost\r\nUser-Agent: python-requests/2.32.3\r\nAccept-Encoding: gzip, deflate, zstd\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Length: 65\r\n\r\nK8rWGbWjWUycQmhKDinJRKVnmTd1ssEfnNbxuzygIK4l1pIVgCGgSx0Kdr3xqIFWK"

需要解析出length和这个随机字符串。因为后续需要通过write写入文件/tmp/xxx。所以,我想到的方法是将这个字符串存入buffer,然后从buffer的最后一个字节开始逆序遍历,到第一个\n停止。这样可以拿到这个随机字符串的逆序,以及用一个寄存器记录一下长度。后续再通过这个长度来再一次逆序这个随机字符串,从而写入/tmp/xxx。最终代码如下:

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
138
139
140
141
.intel_syntax noprefix
.global _start


.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rax, 57 # fork()
syscall

cmp rax, 0
je child_process
mov rdi, 4 # close(4)
mov rax, 3
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall
jmp done

child_process:
mov rdi, 3 # close(3)
mov rax, 3
syscall
mov rdi, 4 # read(4,buf,count_bytes)
lea rsi, buffer
mov rdx, 512
mov rax, 0
syscall
mov r8, rax
lea rsi, buffer+5
lea rdi, file_path
loop_start:
mov al, byte ptr [rsi]
cmp al, 0x20
je get_file_path
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start

get_file_path:
lea rdi, file_path # open("file_path", O_WRONLY|O_CREAT, 0777)
mov rsi, 01|0100
mov rdx, 0x1FF
mov rax, 2
syscall

mov rbx, 0
lea rdi, content
dec r8

loop2_start:
mov al, byte ptr [buffer + r8]
cmp al, 0xa
je get_content_str
mov byte ptr [rdi], al
inc rdi
dec r8
inc rbx
jmp loop2_start

get_content_str:
lea rdi, reverse_content
mov r9, rbx
dec r9
mov rcx, 0
loop3_start:
mov al, byte ptr [content + r9]
cmp rcx, rbx
je get_reverse_str
mov byte ptr [rdi], al
inc rdi
inc rcx
dec r9
jmp loop3_start
get_reverse_str:
mov rdi, 3 # write(3, content, content_length)
lea rsi, reverse_content
mov rdx, rbx
mov rax, 1
syscall

mov rdi, 3 # close(3) = 0
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

done:

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"
buffer:
.space 512
file_path:
.space 64
content:
.space 256
reverse_content:
.space 256

总共三个循环,还是挺复杂的。主要是有更简单的方法。!使用\n的索引即可。如下:

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
.intel_syntax noprefix
.global _start


.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rax, 57 # fork()
syscall

cmp rax, 0
je child_process
mov rdi, 4 # close(4)
mov rax, 3
syscall

mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall
jmp done

child_process:
mov rdi, 3 # close(3)
mov rax, 3
syscall
mov rdi, 4 # read(4,buf,count_bytes)
lea rsi, buffer
mov rdx, 512
mov rax, 0
syscall
mov r10, rax
lea rsi, buffer+5
lea rdi, file_path
loop_start:
mov al, byte ptr [rsi]
cmp al, 0x20
je get_file_path
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start

get_file_path:
lea rdi, file_path # open("file_path", O_WRONLY|O_CREAT, 0777)
mov rsi, 01|0100
mov rdx, 0x1FF
mov rax, 2
syscall
mov r12, 8
mov r8, 0
mov r9, 0
lea rsi, buffer
lea rdi, content
loop2_start:
cmp r8, r12
jge get_content
mov al, byte ptr [rsi]
inc r9
cmp al, '\n'
je get_index
inc rsi
jmp loop2_start
get_index:
inc r8
inc rsi
jmp loop2_start
get_content:
sub r10, r9
mov rdi, 3 # write(3, content, content_length)
lea rsi, buffer
add rsi, r9
mov rdx, r10
mov rax, 1
syscall

mov rdi, 3 # close(3) = 0
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

done:

.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"
buffer:
.space 512
file_path:
.space 64
content:
.space 256
reverse_content:
.space 256

level11

这道题的关键就是多线程,也就是父进程需要不断地接收新的连接,并fork出子进程来处理这个连接,然后再接收新的连接,再fork出子进程来处理。因此,子进程部分就是level9和level10的结合。额外加一个循环,让父进程不断地accept和fork就好了。

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
.intel_syntax noprefix
.global _start


.section .text
_start:
mov rdi, 2 # socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
mov rsi, 1
mov rdx, 0
mov rax, 41
syscall

mov rdi, 3 # bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
lea rsi, [rip+socket_addr]
mov rdx, 16
mov rax, 49
syscall

mov rdi, 3 # listen(3, 0) = 0
mov rsi, 0
mov rax, 50
syscall
loop_accept:
mov rdi, 3 # accept(3, NULL, NULL) = 4
mov rsi, 0
mov rdx, 0
mov rax, 43
syscall

mov rax, 57 # fork()
syscall
mov r8, rax
cmp r8, 0
jne parent_process
cmp r8, 0
je child_process

child_process:
mov rdi, 3 # close(3)
mov rax, 3
syscall
mov rdi, 4 # read(4,buf,count_bytes)
lea rsi, buffer
mov rdx, 512
mov rax, 0
syscall
mov r15, rax
mov al, byte ptr [buffer]
cmp al, 'P'
je post_request
cmp al, 'G'
je get_request

get_request:
lea rsi, buffer+4
lea rdi, file_path_get
loop_start_get:
mov al, byte ptr [rsi]
cmp al, ' '
je get_file_path_get
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start_get

get_file_path_get:
lea rdi, file_path_get # open
mov rsi, 0
mov rax, 2
syscall

mov rdi, 3 # read
mov rsi, rsp
mov rdx, 256
mov rax, 0
syscall
mov r8, rax


mov rdi, 3
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi, 4
mov rsi, rsp
mov rdx, r8
mov rax, 1
syscall


mov rdi,0 # SYS_exit
mov rax,60
syscall



post_request:
mov r10, rax
lea rsi, buffer+5
lea rdi, file_path
loop_start_post:
mov al, byte ptr [rsi]
cmp al, 0x20
je get_file_path_post
mov byte ptr [rdi], al
inc rdi
inc rsi
jmp loop_start_post

get_file_path_post:
lea rdi, file_path # open("file_path", O_WRONLY|O_CREAT, 0777)
mov rsi, 01|0100
mov rdx, 0x1FF
mov rax, 2
syscall
mov r12, 8
mov r8, 0
mov r9, 0
lea rsi, buffer
lea rdi, content
loop2_start_post:
cmp r8, r12
jge get_content
mov al, byte ptr [rsi]
inc r9
cmp al, '\n'
je get_index
inc rsi
jmp loop2_start_post
get_index:
inc r8
inc rsi
jmp loop2_start_post
get_content:
sub r15, r9
mov rdi, 3 # write(3, content, content_length)
lea rsi, buffer
add rsi, r9
mov rdx, r15
mov rax, 1
syscall

mov rdi, 3 # close(3) = 0
mov rax, 3
syscall

mov rdi, 4 # write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
lea rsi,[rip + ret_normal_msg]
mov rdx, 19
mov rax, 1
syscall

mov rdi,0 # SYS_exit
mov rax,60
syscall

parent_process:
mov rdi, 4
mov rax, 3
syscall
jmp loop_accept


.section .data
socket_addr:
.2byte 2
.2byte 0x5000
.4byte 0
.8byte 0
ret_normal_msg:
.ascii "HTTP/1.0 200 OK\r\n\r\n"
buffer:
.space 512
file_path:
.space 64
file_path_get:
.space 64
content:
.space 256
reverse_content:
.space 256


pwn.college: Building a Web Server
https://loboq1ng.github.io/2024/11/10/pwn-college-Building-a-Web-Server/
作者
Lobo Q1ng
发布于
2024年11月10日
许可协议