Featured image of post PwnCollege_Note4

PwnCollege_Note4

ASU CSE 365, building a web server

Building a Web Server

———–ASU CSE 365: Introduction to Cybersecurity

Building a Web Server: Introduction

①Computation

②accessing hardware

instructions should first talk to the OS with the systemcall( syscall like mov rax, 42; syscall) and then OS will operate with the hardware in Kernel

Building a Web Server: Linux Processes

①syscall

read(int fd, void *buf, size_t count) attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

write(int fd, void *buf, size_t count) writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd.

open(char *pathname, int flags, mode_t mode) opens the file specified by pathname. If the specified file doesn’t exist, it may optionally be created(if O_CREAT is specified in flags), the return value of it is a file descriptor that used in subsequent system calls(read(2),write(2),lseek(2),fcntl(2)) to refer to the open file.

②Linux process

struct task_struct *current

this blob of data is living in kernel memory

In the end, the file descriptor is 3 for ‘/flag’ and the RAX is set to 3 as the result of open() syscall . It saved the data into the kernel memory.

③system calls

for more system call can see this

Building a Web Server: Network System Calls

①socket

1
2
int socket(in domain, int type, int protocol)
//socket() creates an endpoint for communication and returns a file descriptor that refers to that endpoint

②bind

1
2
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen)
//when a socket(2) is created with socket, it exits in a name space but has no address assigned to it.bind() assigns the address specified by addr to the socket referred to by the file descriptor sockfd.

③struct sockaddr_in

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct sockaddr{
	uint16_t sa_family;
	uint8_t sa_data[14];
};

struct sockaddr_in{
	uint16_t sin_family;
	uint16_t sin_port;
	uint32_t sin_addr;
	uint8_t __pad[8];
}

AF_INET is 2; htons() function used to convert the number from little endian to big endian for networking work on big ending integers; ipv4 address of 4 bytes is also big endian, the inet_addr() function converts an unsigned integer IP from a host end-order to a network end-order

④listen

1
2
int listen(int sockfd, int backlog)
//listen() marks the socket referred to by sockfd as a passive socket that will be used to accept incoming connection requests using accept(2)

⑤accept

1
2
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
//it's used with connection-based socket types(SOCK_STREAM,SOCK_SEQPACKET).It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket.

⑥accept TCP/IP network connections

Building a Web Server: HTTP

Building a Web Server: Multiprocessing

if there’re lots of http requests, we should another system call.

①fork()

creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.

On success, the PID of the child process is returned to the parent, and 0 is returned in the child.

babyserver

In this series of challenges, we should writing assembly to interact with the environment, and ultimately build a web server

Usage: /challenge/babyserver <path_to_web_server>

level1: exit a program

NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
60 exit man/ cs/ 3C int error_code - - - - -

exit (0) : run the program normally and exit the program

exit (1) : abnormal operation causes the program to exit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.global _start
.intel_syntax noprefix

.section .text

_start:
	mov rdi, 0
	mov rax, 60			# SYS_exit
	syscall
	
.section .data

then use the as (compiler) to generates an object file from the assembly file and use the ld (linker) to link the object file to the library as an executable or library file

1
as -o server.o server.s && ld -o server server.o

strace : tracks system calls and received signals as a process executes

1
2
3
4
hacker@babyserver_level1:~/module4/1$ strace ./server
execve("./server", ["./server"], 0x7ffc21a0ed30 /* 29 vars */) = 0
exit(0)                                 = ?
+++ exited with 0 +++

get flag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/challenge/babyserver ./server
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] exit(0) = ?

===== Trace: Parent Process =====
[] execve("/proc/self/fd/3", ["/proc/self/fd/3"], 0x7f9ad0f54a80 /* 0 vars */) = 0
[] exit(0)                                 = ?
[?] +++ exited with 0 +++

===== Result =====
[] Success

level2: create a socket

NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
41 socket man/ cs/ 29 int int int - - -
1
2
3
4
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] exit(0) = ?

int socket(int domain, int type, int protocol)

need: socket(AF_INET, SOCK_STREAM, IPPROTO_IP)

  • First, we can write it in a c program and look at the errors so that we can put the header files(.h) to c program, seeing each argument

  • then, use the find /usr/include | grep xxx.h and then cat it to find the define things. Strace the c program can find out some syscalls

  • finally, use the objdump -D -M intel xx.out to see the assembly program

(such as the IPPROTO_IP in netinet/in.h is 0)

1
2
3
4
5
6
7
#some useful tool
grep -r 'IPPROTO_IP' /usr/include | grep define
grep -r 'define AF_INET' /usr/include
#define AF_INET PF_INET
grep -r 'define PF_INET' /usr/include
#define PF_INET 2
----------------------------------
1
2
3
import pwn
pwn.constants.AF_INET	#get the constant of AF_INET
print(int(pwn.constants.AF_INET))

so we can get the solution:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.global _start
.intel_syntax noprefix

.section .text

_start:
        mov rdi, 2			;domain:AF_INET
        mov rsi, 1			;type:SOCK_STREAM
        mov rdx, 0 			;protocol:IPPROTO_IP
        mov rax, 41
        syscall				;socket
        mov rdi, 0
        mov rax, 60
        syscall
.section .data

level3: bind an address to a socket

NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
49 bind man/ cs/ 31 struct sockaddr* int - - - -

*int bind(int sockfd, const struct sockaddr addr, socklen_t addrlen)

func: Bind a socket file that specifies the communication protocol to the IP and port

1
2
3
4
5
6
7
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, { sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] exit(0) = ?

we can use the pwntools to set the little ending data(port):

1
2
import pwn
pwn.p16(80,endian="little").hex() #port

the solution:

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

.section .text

_start:
        mov rdi, 2
        mov rsi, 1
        mov rdx, 0 
        mov rax, 41 		#socket
        syscall

        mov rdi, 3
        lea rsi, [rip+sockaddr]			#lea(load effective address)Take the source's offset address
        mov rdx, 16
        mov rax, 49 		#bind
        syscall

        mov rdi, 0
        mov rax, 60 		#exit
        syscall

.section .data
sockaddr:
        .2byte 2			#AF_INET
        .2byte 0x5000		#port:80--->little ending 50 00
        .4byte 0			#addr:0.0.0.0
        .8byte 0

we first find the file /usr/include/netinet/in.h and find out the sockaddr_in struct

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct sockaddr_in
{
	__SOCKADDR_COMMON (sin_);
    in_port_t sin_port;                 /* Port number.  */
    struct in_addr sin_addr;            /* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr)
                           - __SOCKADDR_COMMON_SIZE
                           - sizeof (in_port_t)
                           - sizeof (struct in_addr)];
};

and then we step by step find out the size of every type of data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#define __SOCKADDR_COMMON_SIZE        (sizeof (unsigned short int))		//2bytes

/* Type to represent a port.  */
typedef uint16_t in_port_t;		//2bytes

/* Internet address.  */
typedef uint32_t in_addr_t;		//4bytes
struct in_addr
{
    in_addr_t s_addr;
};

level4: listen on a socket

1
2
3
4
5
6
7
8
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] listen(3, 0) = 0
[ ] exit(0) = ?
NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
50 listen man/ cs/ 32 int int - - - -

int listen(int sockfd, int backlog)

1
2
3
4
mov rdi, 3
mov rsi, 0
mov rax, 50
syscall

level4: accept a connection

1
2
3
4
5
6
7
8
9
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] listen(3, 0) = 0
[ ] accept(3, NULL, NULL) = 4
[ ] exit(0) = ?
NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
43 accept man/ cs/ 2B int struct sockaddr * int * - - -

*int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen)

I write a c program about the accept and objdump it, then I find that the NULL should only set to the 0x0 to the register. It works.

1
2
3
4
5
mov rdi, 3
mov rsi, 0x0
mov rdx, 0x0
mov rax, 43 #accept
syscall

level6: respond to an http request

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] listen(3, 0) = 0
[ ] accept(3, NULL, NULL) = 4
[ ] read(4, <read_request>, <read_request_count>) = <read_request_result>
[ ] write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
[ ] close(4) = 0
[ ] exit(0) = ?
NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
0 read man/ cs/ 0 unsigned int fd char *buf size_t count - - -
1 write man/ cs/ 1 unsigned int fd const char *buf size_t count - - -
3 close man/ cs/ 3 unsigned int fd - - - - -

ssize_t read(int fd, void *buf, size_t count);

ssize_t write (int filedes, void * buf, size_t nbytes);

int close(int fd);

Only show the newly added code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
        mov rdi, 4
        mov rsi, rsp
        mov rdx, 256
        mov rax, 0 #read
        syscall

        mov rdi, 4
        lea rsi, [rip+msg]
        mov rdx, 19
        mov rax, 1 #write
        syscall

        mov rdi, 4
        mov rax, 3 #close
        syscall
.section .data
msg:
        .ascii "HTTP/1.0 200 OK\r\n\r\n"

level7: respond to a GET request for the contents of a specified file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] listen(3, 0) = 0
[ ] accept(3, NULL, NULL) = 4
[ ] read(4, <read_request>, <read_request_count>) = <read_request_result>
[ ] open("<open_path>", O_RDONLY) = 5
[ ] read(5, <read_file>, <read_file_count>) = <read_file_result>
[ ] close(5) = 0
[ ] write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
[ ] write(4, <write_file>, <write_file_count> = <write_file_result>
[ ] close(4) = 0
[ ] exit(0) = ?

find out the definition of the O_RDONLY:

1
2
hacker@babyserver_level7:~/module4/7$ grep -r "O_RDONLY" /usr/include/
/usr/include/x86_64-linux-gnu/bits/fcntl-linux.h:#define O_RDONLY            00

There’re something to mention: the <open_path> should be the file that accept from the client and it is automatic, so we can’t put a casual path to the open(). The second thing is that we must get the right content of the file so we should save the count after reading from the file.

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

.section .text

_start:
        mov rdi, 2
        mov rsi, 1
        mov rdx, 0 
        mov rax, 41 #socket
        syscall

        mov rdi, 3
        lea rsi, [rip+sockaddr]
        mov rdx, 16
        mov rax, 49 #bind
        syscall

        mov rdi, 3
        mov rsi, 0
        mov rax, 50 #listen
        syscall

        mov rdi, 3
        mov rsi, 0x0
                mov rdx, 0x0
        mov rax, 43 #accept
        syscall

        mov rdi, 4
        mov rsi, rsp
        mov rdx, 0x1000
        mov rax, 0 #read
        syscall
#GET /temp/xxxx HTTP/1.0
#      		   ^
# r10		   \0

#got the /temp/xxxx<----automatic

loop:
        mov al, [rsp]
        cmp al, ' '
        je next
        inc rsp
        jmp loop
next:
        inc rsp
        mov r10, rsp

loop2:
        mov al, [rsp]
        cmp al, ' '
        je next2
        inc rsp
        jmp loop2
next2:
        mov byte ptr [rsp], 0
        mov rdi, r10
        #lea rdi, [rip+filepath]
        mov rsi, 0
        mov rax, 2 #open
        syscall

        mov rdi, 5
        mov rsi, rsp
        mov rdx, 0x1000
        mov rax, 0 #read
        syscall
        mov r12, rax		#tried the r11 will have errors so change to the r12
        					#save the size returning from the read func

        mov rdi, 5
        mov rax, 3
        syscall

        mov rdi, 4
        lea rsi, [rip+msg]
        mov rdx, 19
        mov rax, 1 #write
        syscall

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

        mov rdi, 4
        mov rax, 3 #close
        syscall

        mov rdi, 0
        mov rax, 60 #exit
        syscall

.section .data
sockaddr:
        .2byte 2
        .2byte 0x5000
        .4byte 0
        .8byte 0
msg:
        .ascii "HTTP/1.0 200 OK\r\n\r\n"
filepath:
        .ascii "/tmp/"		#didn't use

level8: accept multiple requests

Compare with the level7, delete the exit and add the accept again

level9: concurrently accept multiple requests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] listen(3, 0) = 0
[ ] accept(3, NULL, NULL) = 4
[ ] fork() = <fork_result>
[ ] close(4) = 0
[ ] accept(3, NULL, NULL) = ?

===== Expected: Child Process =====
[ ] close(3) = 0
[ ] read(4, <read_request>, <read_request_count>) = <read_request_result>
[ ] open("<open_path>", O_RDONLY) = 3
[ ] read(3, <read_file>, <read_file_count>) = <read_file_result>
[ ] close(3) = 0
[ ] write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
[ ] write(4, <write_file>, <write_file_count> = <write_file_result>
[ ] exit(0) = ?
NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
57 fork man/ cs/ 39 - - - - - -

pid_t fork(void);

For the parent process it will get the ID or return Number of the new created child processes and the child process get the 0 ID so we need to use conditional jumps to distinguish between them.

1
2
3
4
5
6
7
8
mov rax, 57 #fork
syscall

mov r8, rax
cmp r8, 7
je parent
cmp r8, 0
je child
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
===== Trace: Parent Process =====
[] execve("/proc/self/fd/3", ["/proc/self/fd/3"], 0x7f6c58e3aaa0 /* 0 vars */) = 0
[] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[] bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
[] listen(3, 0)                            = 0
[] accept(3, NULL, NULL)                   = 4
[] fork()                                  = 7
[] close(4)                                = 0
[] accept(3, NULL, NULL)                   = ?
[?] +++ killed by SIGKILL +++

===== Trace: Child Process =====
[] close(3)                                = 0
[] read(4, "GET /tmp/tmpq9rbaedq HTTP/1.1\r\nHost: localhost\r\nUser-Agent: python-requests/2.28.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n", 4096) = 155
[] open("/tmp/tmpq9rbaedq", O_RDONLY)      = 3
[] read(3, "ppNPPIPL1y3rs5lyygp2Odm7P8TiMzFLKR1UyCySEFsG4uIHLByLEXMHsblOhc3kJOtwK30IGN6RUOt1JnTTVTiDXI4Db7ormkmN3up7rwCcLqhJk74J4rr4cYRbWta7RlJNciFqocdfIuTNMe4mIkg3XtItfDI2LZV5KN93", 4096) = 168
[] close(3)                                = 0
[] write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
[] write(4, "ppNPPIPL1y3rs5lyygp2Odm7P8TiMzFLKR1UyCySEFsG4uIHLByLEXMHsblOhc3kJOtwK30IGN6RUOt1JnTTVTiDXI4Db7ormkmN3up7rwCcLqhJk74J4rr4cYRbWta7RlJNciFqocdfIuTNMe4mIkg3XtItfDI2LZV5KN93", 168) = 168
[] exit(0)                                 = ?
[?] +++ exited with 0 +++

===== Result =====
[] Success

level10: respond to a POST request with a specified file and update its contents

1
2
3
4
5
6
7
8
===== Expected: Child Process =====
[ ] close(3) = 0
[ ] read(4, <read_request>, <read_request_count>) = <read_request_result>
[ ] open("<open_path>", O_WRONLY|O_CREAT, 0777) = 3
[ ] write(3, <write_file>, <write_file_count> = <write_file_result>
[ ] close(3) = 0
[ ] write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
[ ] exit(0) = ?
NR SYSCALL NAME references RAX ARG0(rdi) ARG1(rsi) ARG2(rdx) ARG3(r10) ARG4(r8) ARG5(r9)
2 open man/ cs/ 2 const char *filename int flags umode_t mode - - -

int open(const char *pathname, int flags, mode_t mode);

1
2
/usr/include/x86_64-linux-gnu/bits/fcntl-linux.h:# define O_CREAT          0100 /* Not fcntl.  */
/usr/include/x86_64-linux-gnu/bits/fcntl-linux.h:#define O_WRONLY            01

the 3rd argument of 0777 is OCT so we need to change it to the HEX 1FF.

POST:

and we should do in level10 is :

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

.section .text

_start:
        mov rdi, 2
        mov rsi, 1
        mov rdx, 0 
        mov rax, 41 #socket
        syscall
	
        mov rdi, 3
        lea rsi, [rip+sockaddr]
        mov rdx, 16	
        mov rax, 49 #bind
        syscall

        mov rdi, 3
        mov rsi, 0
        mov rax, 50 #listen
        syscall

        mov rdi, 3
        mov rsi, 0x0
        mov rdx, 0x0
        mov rax, 43 #accept
        syscall	

        mov rax, 57 #fork
        syscall

        mov r8, rax
        cmp r8, 7
        je parent
        cmp r8, 0
        je child
parent:
        mov rdi, 4
        mov rax, 3 #close
        syscall

        mov rdi, 3
        mov rsi, 0x0
        mov rdx, 0x0
        mov rax, 43 #accept
        syscall	
child:
        mov rdi, 3
        mov rax, 3 #close
        syscall
	
        mov rdi, 4
        mov rsi, rsp
        mov rdx, 0x1000
        mov rax, 0 #read
        syscall
        mov r14, rax	
        sub r14, 177		#calculate the size
loop:
        mov al, [rsp]
        cmp al, ' '
        je next
        inc rsp
        jmp loop
next:
        inc rsp
        mov r10, rsp

loop2:
        mov al, [rsp]
        cmp al, ' '
        je next2
        inc rsp
        jmp loop2
next2:
        mov byte ptr [rsp], 0
        mov rdi, r10
        mov rsi, 0100|01		#O_WRONLY|O_CREAT
        mov rdx, 0x1ff
        mov rax, 2 #open
        syscall
	
loop3:
        mov al, [rsp]	#must in the loop
        cmp al, 'H'
        je next3
        inc rsp
        jmp loop3
next3:
        inc rsp
loop4:
        mov al, [rsp]
        cmp al, 'L'
        je next4
        inc rsp
        jmp loop4
next4:
        add rsp, 15		#calculate the size
        mov r8, rsp

        mov rdi, 3
        mov rsi, r8
        mov rdx, r14
        mov rax, 1 #write
        syscall

        mov rdi, 3
        mov rax, 3 #close
        syscall

        mov rdi, 4
        lea rsi, [rip+msg]
        mov rdx, 19
        mov rax, 1 #write
        syscall

        mov rdi, 0
        mov rax, 60 #exit
        syscall
.section .data
sockaddr:
        .2byte 2
        .2byte 0x5000
        .4byte 0
        .8byte 0
msg:
        .ascii "HTTP/1.0 200 OK\r\n\r\n"

emmmm, I feel like I’m using a little trick…..Sometimes it is wrong according to the counts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
===== Trace: Parent Process =====
[] execve("/proc/self/fd/3", ["/proc/self/fd/3"], 0x7fbd50e91aa0 /* 0 vars */) = 0
[] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[] bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
[] listen(3, 0)                            = 0
[] accept(3, NULL, NULL)                   = 4
[] fork()                                  = 7
[] close(4)                                = 0
[] accept(3, NULL, NULL)                   = ?
[?] +++ killed by SIGKILL +++

===== Trace: Child Process =====
[] close(3)                                = 0
[] read(4, "POST /tmp/tmpvx_ye4_7 HTTP/1.1\r\nHost: localhost\r\nUser-Agent: python-requests/2.28.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Length: 212\r\n\r\nz7sUJ0m2d9IkQeghmuUTlpCXBPRbQGKpmILxptSYl9aJhMwGgC7PQpSTMR8qTr2q8uAfzsro0y9fSbM8anzW7BsK10vsfaOThRBw4B6R5y38IOedthK0iOoX5ymIWbuBnOBPgsCNh5C45wcMDNR9LFEfUGTz7bvFCHRQlFg4LXZNazadwka8pN7A0QsBhgmRHQ4VOg5iPqBiGeMpX2gD", 4096) = 389
[] open("/tmp/tmpvx_ye4_7", O_WRONLY|O_CREAT, 0777) = 3
[] write(3, "z7sUJ0m2d9IkQeghmuUTlpCXBPRbQGKpmILxptSYl9aJhMwGgC7PQpSTMR8qTr2q8uAfzsro0y9fSbM8anzW7BsK10vsfaOThRBw4B6R5y38IOedthK0iOoX5ymIWbuBnOBPgsCNh5C45wcMDNR9LFEfUGTz7bvFCHRQlFg4LXZNazadwka8pN7A0QsBhgmRHQ4VOg5iPqBiGeMpX2gD", 212) = 212
[] close(3)                                = 0
[] write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
[] exit(0)                                 = ?
[?] +++ exited with 0 +++

===== Result =====
[] Success

OK, fine…It turns out that we cannot be opportunistic. I modified the code to make it possible to judge three-digit numbers as well as two-digit numbers so that it won’t have errors several times. And this is good for editing the level11.

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

.section .text

_start:
	mov rdi, 2
	mov rsi, 1
	mov rdx, 0 
	mov rax, 41 #socket
	syscall
	
	mov rdi, 3
	lea rsi, [rip+sockaddr]
	mov rdx, 16	
	mov rax, 49 #bind
	syscall

	mov rdi, 3
	mov rsi, 0
	mov rax, 50 #listen
	syscall
	
	mov rdi, 3
	mov rsi, 0x0
	mov rdx, 0x0
	mov rax, 43 #accept
	syscall	

	mov rax, 57 #fork
	syscall
	
	mov r8, rax
	cmp r8, 7
	je parent
	cmp r8, 0
	je child
parent:
	mov rdi, 4
	mov rax, 3 #close
	syscall

	mov rdi, 3
	mov rsi, 0x0
	mov rdx, 0x0
	mov rax, 43 #accept
	syscall	
child:
	mov rdi, 3
	mov rax, 3 #close
	syscall
	
	mov rdi, 4
	mov rsi, rsp
	mov rdx, 0x1000
	mov rax, 0 #read
	syscall
	xor r12, r12
	mov r12, rax	
	
loop:
	mov al, [rsp]
	cmp al, ' '
	je next
	inc rsp
	jmp loop
next:
	inc rsp
	mov r10, rsp

loop2:
	mov al, [rsp]
	cmp al, ' '
	je next2
	inc rsp
	jmp loop2
next2:
	mov byte ptr [rsp], 0
	mov rdi, r10
	mov rsi, 0100|01
	mov rdx, 0x1ff
	mov rax, 2 #open
	syscall
	
loop3:
	mov al, [rsp]
	cmp al, 'H'
	je next3
	inc rsp
	jmp loop3
next3:
	inc rsp
loop4:
	mov al, [rsp]
	cmp al, 'L'
	je next4
	inc rsp
	jmp loop4
next4:
loop5:
	mov al, [rsp]
	cmp al, ' '
	je next5
	inc rsp
	jmp loop5
next5:
	xor r15, r15 #at the beginning it is in loop6 so r15 is always 0...
	mov r15, 0
loop6:
	mov al, [rsp]
	cmp al, '\r'
	je next6
	inc rsp
	add r15, 1
	jmp loop6
next6:
	add rsp, 4
	mov r8, rsp	
	cmp r15, 3 #two is 3 and three is 4!!
	jne three

two:#two-digit
	xor rdx, rdx
	sub r12, 176
	mov rdi, 3
	mov rsi, r8
	mov rdx, r12
	mov rax, 1 #write
	syscall

	mov rdi, 3
	mov rax, 3 #close
	syscall

	mov rdi, 4
	lea rsi, [rip+msg]
	mov rdx, 19
	mov rax, 1 #write
	syscall

	mov rdi, 0
	mov rax, 60 #exit
	syscall

	jmp done

three:#three-digit
	xor rdx, rdx
	sub r12, 177
	mov rdi, 3
	mov rsi, r8
	mov rdx, r12
	mov rax, 1 #write
	syscall

	mov rdi, 3
	mov rax, 3 #close
	syscall

	mov rdi, 4
	lea rsi, [rip+msg]
	mov rdx, 19
	mov rax, 1 #write
	syscall

done:
	mov rdi, 0
	mov rax, 60 #exit
	syscall


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

level11: respond to multiple concurrent GET and POST requests

(still trying…) (x)

(figure out!) (√)

The post was wrong at first. So I spent some time revising it. And then get is right. To understand the whole process and continue programming, it will be more effective with less effort.

  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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
.global _start
.intel_syntax noprefix

.section .text

_start:
		mov rdi, 2
		mov rsi, 1
		mov rdx, 0 
		mov rax, 41 #socket
		syscall
		
		mov rdi, 3
		lea rsi, [rip+sockaddr]
		mov rdx, 16	
		mov rax, 49 #bind
		syscall

		mov rdi, 3
		mov rsi, 0
		mov rax, 50 #listen
		syscall
		#xor r14, r14
		#mov r14, 10
loopp:	#this is important!! after socket,bind,listen should loop in accept and fork,continue forking to accept multiple requests of post and get
		mov rdi, 3
		mov rsi, 0x0
		mov rdx, 0x0
		mov rax, 43 #accept
		syscall
                
		mov rax, 57 #fork
		syscall
		
		mov r8, rax
		cmp r8, 0
		jne parent
		cmp r8, 0
		je child

child:
		mov rdi, 3
		mov rax, 3 #close
		syscall
	
		mov rdi, 4
		mov rsi, rsp
		mov rdx, 0x1000
		mov rax, 0 #read
		syscall
		
		mov cl, [rsp] #post or get
		cmp cl, 'P'
		je post
		cmp cl, 'G'
		je get

get:

getloop:
		mov al, [rsp]
		cmp al, ' '
		je getnext
		inc rsp
		jmp getloop
getnext:
		inc rsp
		mov r10, rsp

getloop2:
		mov al, [rsp]
		cmp al, ' '
		je getnext2
		inc rsp
		jmp getloop2
getnext2:
		mov byte ptr [rsp], 0
		mov rdi, r10
		#lea rdi, [rip+filepath]
		mov rsi, 0
		mov rax, 2 #open
		syscall

		mov rdi, 3
		mov rsi, rsp
		mov rdx, 0x1000
		mov rax, 0 #read
		syscall
		mov r12, rax

		mov rdi, 3
		mov rax, 3
		syscall

		mov rdi, 4
		lea rsi, [rip+msg]
		mov rdx, 19
		mov rax, 1 #write
		syscall

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

		mov rdi, 0
		mov rax, 60 #exit
		syscall


post:
		xor r12, r12
		mov r12, rax	
	
loop:
		mov al, [rsp]
		cmp al, ' '
		je next
		inc rsp
		jmp loop
next:
		inc rsp
		mov r10, rsp

loop2:
		mov al, [rsp]
		cmp al, ' '
		je next2
		inc rsp
		jmp loop2
next2:
		mov byte ptr [rsp], 0
		mov rdi, r10
		mov rsi, 0100|01
		mov rdx, 0x1ff
		mov rax, 2 #open
		syscall
	
loop3:
		mov al, [rsp]
		cmp al, 'H'
		je next3
		inc rsp
		jmp loop3
next3:
		inc rsp
loop4:
		mov al, [rsp]
		cmp al, 'L'
		je next4
		inc rsp
		jmp loop4
next4:
loop5:
		mov al, [rsp]
		cmp al, ' '
		je next5
		inc rsp
		jmp loop5
next5:
		xor r15, r15
		mov r15, 0
loop6:
                
		mov al, [rsp]
		cmp al, '\r'
		je next6
		inc rsp
		add r15, 1
		jmp loop6
next6:
		add rsp, 4
		mov r8, rsp	
		cmp r15, 3
		jne three
two:
		xor rdx, rdx
		sub r12, 176
		mov rdi, 3
		mov rsi, r8
		mov rdx, r12
		mov rax, 1 #write
		syscall

		mov rdi, 3
		mov rax, 3 #close
		syscall

		mov rdi, 4
		lea rsi, [rip+msg]
		mov rdx, 19
		mov rax, 1 #write
		syscall

		mov rdi, 0
		mov rax, 60 #exit
		syscall

		jmp done

three:
		xor rdx, rdx
		mov rdi, 3
		mov rsi, r8
		mov rdx, r12
		sub rdx, 177
		mov rax, 1 #write
		syscall

		mov rdi, 3
		mov rax, 3 #close
		syscall

		mov rdi, 4
		lea rsi, [rip+msg]
		mov rdx, 19
		mov rax, 1 #write
		syscall

done:
		mov rdi, 0
		mov rax, 60 #exit
		syscall

		jmp donex

parent:
		mov rdi, 4			#The 2nd error is that parent needn't accept but jump to loop accept
		mov rax, 3 #close
		syscall

		#mov rdi, 3
		#mov rsi, 0x0
		#mov rdx, 0x0
		#mov rax, 43 #accept
		#syscall	

		#sub r14, 1
		#cmp r14, 0
		jmp loopp

donex:

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

**for debugging: **

1
2
3
4
5
6
#in the first terminal:
strace -f(child process) ./server
#in the second terminal:
nc 127.0.0.1 80
GET /tmp/abc 
POST /tmp/abc ...
Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy
Caret Up