├── .gdb_history ├── LICENSE ├── README.md ├── README_ZH.md ├── hosts.txt ├── makefile ├── src ├── logger.c ├── logger.h ├── test_pwn.c ├── waf.c └── waf.h └── test /.gdb_history: -------------------------------------------------------------------------------- 1 | b execve 2 | c 3 | start 4 | c 5 | si 6 | si 7 | si 8 | si 9 | asdfasd 10 | si 11 | si 12 | q 13 | b main 14 | start 15 | si 16 | si 17 | finish 18 | si 19 | finish 20 | si 21 | finish 22 | si 23 | finish 24 | si 25 | q 26 | si 27 | start 28 | si 29 | si 30 | si 31 | finish 32 | si 33 | finish 34 | si 35 | finish 36 | si 37 | finish 38 | si 39 | finish 40 | si 41 | finish 42 | si 43 | finish 44 | si 45 | finish 46 | si 47 | finish 48 | si 49 | si 50 | si 51 | si 52 | si 53 | finish 54 | si 55 | finish 56 | si 57 | si 58 | si 59 | finish 60 | si 61 | si 62 | q 63 | b execve 64 | si 65 | start 66 | si 67 | si 68 | c 69 | q 70 | b main 71 | start 72 | si 73 | finsih 74 | finish 75 | si 76 | finish 77 | si 78 | finish 79 | si 80 | finish 81 | si 82 | finish 83 | si 84 | finish 85 | si 86 | finish 87 | si 88 | finish 89 | si 90 | finish 91 | si 92 | si 93 | si 94 | si 95 | si 96 | ls 97 | ls 98 | exit 99 | q 100 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 i0gan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWD PWN WAF 2 | 3 | [中文版](./README_ZH.md) 4 | 5 | CTF AWD WAF FOR PWN 6 | 7 | Author: I0gan 8 | 9 | Mail: i0gan@pwnsky.com 10 | 11 | ## Intro 12 | 13 | The WAF is a traffic capture tool for AWD PWN, which is convenient to analyze and write anti attack exp, and is very conducive to the use of `PWN Ctfer`. The tool has been used in AWD competitions for many times. I hope you can `give me a star`. The WAF has `four modes`. 14 | 15 | 1. The `CATCH` mode simply capture the attacked interactive traffic, which can be viewed under the `log path`. 16 | 17 | 2. The `I0GAN` mode is a `defense mode`, which can prevent attackers from getting the shell, and can also view the attacker's interactive traffic. This mode is used cautiously, strictly abides by the rules of the ctf competition. If you violates the rules of the competition. The consequences are borne by yourslves. 18 | 19 | 3. The `FORWARD` [Recommend] mode simply forwards the attacker's traffic to hit others, and we can capture the traffic in the middle. Of course, if the attacker successfully gets the flag, We can also get the flag in the log file. 20 | 21 | 4. The `FORWARD_MUTIL` is based on `FORWARD` mode, it mainly loop get victims's host information from `hosts.txt` file then forward attacker's traffic to victims. 22 | 23 | ## Contributor 24 | 25 | ``` 26 | i0gan: Code writer 27 | b0ldfrev: Commit IO blocking bug 28 | moon: Commit Chinese Document 29 | ``` 30 | 31 | ## Fix Log 32 | 2021-05-13: Thancks b0ldfrev commit the IO blocking problem for FORWARD mode 33 | 34 | 35 | 36 | ## CATCH / I0GAN modes waf principle 37 | 38 | Execve the target elf file by creating a child process, and then the parent process uses ptrace to monitor the syscall of the child process. If the standard IO is used, the data is read and recorded in the log. If the syscall is dangerous, it is also recorded in the log 39 | 40 | 41 | 42 | ## FORWARD / FORWARD_MULTI modes waf principle 43 | 44 | Capture traffic from standard I / O and forward it to the target server. You can captrue traffic between the attacker and the victim, and you can also get victim's flag. 45 | 46 | 47 | 48 | 49 | ## Code Tree 50 | 51 | ``` 52 | src 53 | ├── logger.c 54 | ├── logger.h 55 | ├── test_pwn.c 56 | ├── waf.c 57 | └── waf.h 58 | ``` 59 | 60 | 61 | 62 | ## How to use? 63 | 64 | ### 0x00 Know your server 65 | 66 | After ssh to your server, checking your server for monitored program. 67 | 68 | ``` 69 | cat /etc/xinetd.d/pwn 70 | 71 | service pwn 72 | { 73 | disable = no 74 | socket_type = stream 75 | protocol = tcp 76 | wait = no 77 | user = pwn 78 | type = UNLISTED 79 | port = 80 80 | bind = 0.0.0.0 81 | server = /pwn 82 | server_args = none 83 | # safety options 84 | per_source = 10 # the maximum instances of this service per source IP address 85 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 86 | rlimit_as = 256M # the Address Space resource limit for the service 87 | #access_times = 8:50-17:10 88 | } 89 | ``` 90 | 91 | So monitored program is `/pwn`. 92 | 93 | If the server program is `chroot`, you should notice something, like this 94 | 95 | ``` 96 | service pwn 97 | { 98 | disable = no 99 | flags = REUSE 100 | socket_type = stream 101 | protocol = tcp 102 | wait = no 103 | user = root 104 | type = UNLISTED 105 | port = 80 106 | 107 | bind = 0.0.0.0 108 | server = /usr/sbin/chroot 109 | server_args = --userspec=1000:1000 /home/pwn ./pwn 110 | # safety options 111 | per_source = 5 # the maximum instances of this service per source IP address 112 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 113 | rlimit_as = 100M # the Address Space resource limit for the service 114 | #access_times = 8:50-17:10 115 | } 116 | ``` 117 | 118 | Like this situation, the monitored program is `/home/pwn/pwn` 119 | 120 | Next, we should found a path that have read && write permissions to `pwn` user. 121 | 122 | Use `touch` command to test then find this path. 123 | 124 | In general, this condition will be satisfied in the `/tmp` directory. But if server program is `chroot` you should find this path in `chroot args1` [/home/pwn]. 125 | 126 | If not found path, this waf just can be used in `RUN_FORWARD` mode. 127 | 128 | 129 | 130 | replace this program. 131 | 132 | You should replace /home/pwn/pwn to our waf program. 133 | 134 | 135 | 136 | ### 0x01 Configure waf 137 | 138 | Some configuration information of all modes is in `makefile` 139 | 140 | #### CATCH and I0GAN mode configure 141 | 142 | In makefile 143 | 144 | ``` 145 | # configure 146 | # log path 147 | LOG_METHOD := OPEN 148 | LOG_PATH := /tmp/.waf 149 | ARCH := 64 150 | ``` 151 | 152 | `LOG_PATH` is a log file path 153 | 154 | `ARCH` Represents whether the program is 32-bit or 64 bit. 155 | 156 | You shold set `LOG_PATH` to your path that have read && write permissions in `pwn` user. 157 | 158 | #### FORWARD mode configure 159 | 160 | ``` 161 | # configure 162 | # log path 163 | LOG_METHOD := OPEN 164 | LOG_PATH := /tmp/.waf 165 | ARCH := 64 166 | 167 | # Just used in FORWARD mode 168 | FORWARD_IP := 127.0.0.1 169 | FORWARD_PORT := 20000 170 | ``` 171 | 172 | `LOG_PATH` is a log file path 173 | 174 | `ARCH` Represents whether the program is 32-bit or 64 bit. 175 | 176 | `FORWARD_IP` is static victim's ip 177 | 178 | `FORWARD_PORT` is static victim's port 179 | 180 | You shold set `LOG_PATH` to your path that have read && write permissions in `pwn` user. 181 | 182 | If you not have permissions to write anaything, You should close logger. Modify makefile 183 | 184 | ``` 185 | LOG_METHOD := CLOSE 186 | ``` 187 | 188 | So this mode is not rely on other file. you can use this mode to avoid attacked from attacker. 189 | 190 | #### FOWARD_MULTI mode configure 191 | 192 | If you use `RUN_FORWARD_MULTI` mode, you must set hosts information in hosts.txt, like 193 | 194 | hosts.txt 195 | 196 | ``` 197 | 127.0.0.1:20000 198 | 127.0.0.1:8081 199 | ``` 200 | 201 | And set log path in `makefile` 202 | 203 | ``` 204 | # configure 205 | # log path 206 | LOG_PATH := /tmp/.waf 207 | ARCH := 64 208 | ``` 209 | 210 | `LOG_PATH` is a log file path 211 | 212 | `ARCH` Represents whether the program is 32-bit or 64 bit. 213 | 214 | You shold set `LOG_PATH` to your path that have read && write permissions in `pwn` user. 215 | 216 | 217 | 218 | ### 0x02 Compile 219 | 220 | #### Compile all 221 | 222 | #### Notice 223 | 224 | Compile Env glibc version must <= target glibc version 225 | Or waf cannot execute 226 | ``` 227 | ./i0gan: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./i0gan) 228 | 229 | ``` 230 | 231 | 232 | ``` 233 | make # or make all 234 | ``` 235 | 236 | or 237 | 238 | ``` 239 | make catch 240 | make i0gan 241 | make forward 242 | make forward_multi 243 | ``` 244 | 245 | 246 | 247 | You will get `catch`,`i0gan`,`redir` files 248 | 249 | The `catch` is `RUN_CATCH` mode waf program 250 | 251 | The `i0gan` is `RUN_I0GAN` mode waf program 252 | 253 | The `forward` is `RUN_FORWARD` mode waf program 254 | 255 | The `forward_mutil` is `RUN_FORWARD_MULTI` mode waf program 256 | 257 | #### simple test 258 | 259 | After compiling all successfully, you can run `./catch` or ` ./i0gan` directly to test 260 | Store the interactive log file in the directory `/tmp/.waf/` , The name of file format is `time + hex microseconds + .log` 261 | 262 | ### 0x03 Add waf to server 263 | 264 | The `pwn service paths` stored in different events may be different. Take `/pwn/pwn` as an example. If you don't know where it is, `cat /etc/xinetd.d/pwn` service can view its own service path. The `LOG_PATH` you should find by yourself that have `read && write` permissions in `pwn` user. Here take `LOG_PATH` is `/tmp/.waf` as an example. 265 | 266 | #### CATCH or I0GAN mode 267 | 268 | Upload the compiled `catch` or `i0gan` program file to the `/tmp` directory. 269 | 270 | 1. Copy the monitored service program to `LOG_PATH` directory. 271 | 272 | 2. Replace the service program with WAF program 273 | 274 | ``` 275 | mkdir /tmp/.waf # Create LOG_PATH directory 276 | chmod 777 /tmp/.waf # Modify permissions 277 | cp /pwn/pwn /tmp/.waf # Move service binary program to /tmp/.i0gan dirctory 278 | chown pwn:pwn /tmp/.waf/pwn # Chown as pwn 279 | chmod 777 /tmp/.waf/pwn 280 | mv /tmp/catch /pwn/pwn # Replace your service binary program to our waf 281 | chmod 777 /pwn/pwn 282 | ``` 283 | 284 | 285 | Notice: 286 | You must use nc to test. If server is down, checking your listened pwn program's permission and log permission. 287 | If your program is orw type, open fd start with 6, not 3 288 | 289 | If the attacker attacks, the corresponding attack log file will be generated in the directory `/tmp/.i0gan/`. Each attack will generate a file, which can be directly analyzed after being attacked 290 | 291 | #### FOWARD mode 292 | 293 | Upload the compiled ` forward` program file to the `/tmp` directory. 294 | 295 | If your server have not permissions to store log, you should Close log, just set `LOG_METHOD := OPEN` in makefile and recompile forward. 296 | 297 | 1. Replace the service program with WAF program 298 | 299 | ``` 300 | cp /tmp/forward /pwn/pwn # Replace your service binary program to our waf 301 | chmod 777 /pwn/pwn 302 | ``` 303 | 304 | 305 | 306 | #### FOWARD_MULTI mode 307 | 308 | Upload the compiled ` forward_multi` and `hosts.txt` files to the `/tmp` directory. 309 | 310 | 1. Copy `hosts.txt` under `LOG_PATH` 311 | 2. Replace the service program with WAF program 312 | 313 | ``` 314 | mkdir /tmp/.waf # Create LOG_PATH directory 315 | chmod 777 /tmp/.waf # Modify permissions 316 | cp /tmp/hosts.txt /tmp/.waf # Upload hostes.txt under LOG_PATH 317 | cp /tmp/forward_multi /pwn/pwn # Replace your service binary program to our waf 318 | chmod 777 /pwn/pwn 319 | ``` 320 | 321 | Notice: 322 | You must use nc to test. Check your log permission 323 | 324 | 325 | ### 0x04 Test your server 326 | 327 | Note that you need to test your service 328 | 329 | ``` 330 | nc your_server_ip your_server_port 331 | ``` 332 | 333 | The common problem is that the permissions are not enough, which appear in the `log path` without permissions. 334 | 335 | 336 | 337 | ## Test CATCH 338 | 339 | server 340 | 341 | ``` 342 | socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./catch 343 | ``` 344 | 345 | attacker 346 | 347 | ``` 348 | nc 127.0.0.1 1234 349 | Test puts: 350 | Test write� 351 | Test read: 352 | abcasdfasdf 353 | Test gets: 354 | 355 | asdfadf 356 | Test get shell: 357 | 358 | ls 359 | README.md 360 | catch 361 | forward 362 | forward_multi 363 | hosts.txt 364 | i0gan 365 | makefile 366 | src 367 | test 368 | test_pwn 369 | exit 370 | ``` 371 | 372 | 373 | 374 | server log 375 | 376 | ``` 377 | cat /tmp/.waf/14_32_43_6740c.log 378 | ``` 379 | 380 | 381 | 382 | ``` 383 | // Date: 2021-06-20 14:32:43 384 | // Mode: CATCH 385 | // CTF AWD PWN WAF 386 | // Deved By I0gan 387 | 388 | <-------------------- write -----------------> 389 | Test puts: 390 | Test write� 391 | Test read: 392 | 393 | w_0 = "\x54\x65\x73\x74\x20\x70\x75\x74\x73\x3a\x0a\x54\x65\x73\x74\x20\x77\x72\x69\x74\x65\x00\x01\x02\x03\xff\x0a\x54\x65\x73\x74\x20\x72\x65\x61\x64\x3a\x0a" 394 | 395 | <-------------------- read ------------------> 396 | abcasdfasdf 397 | 398 | r_0 = "\x61\x62\x63\x61\x73\x64\x66\x61\x73\x64\x66\x0a" 399 | 400 | <-------------------- write -----------------> 401 | Test gets: 402 | 403 | 404 | w_1 = "\x54\x65\x73\x74\x20\x67\x65\x74\x73\x3a\x0a\x0a" 405 | 406 | <-------------------- read ------------------> 407 | asdfadf 408 | 409 | r_1 = "\x61\x73\x64\x66\x61\x64\x66\x0a" 410 | 411 | <-------------------- write -----------------> 412 | Test get shell: 413 | 414 | 415 | <-------------- dangerous syscall------------> 416 | <-------------- dangerous syscall------------> 417 | <-------------- dangerous syscall------------> 418 | w_2 = "\x54\x65\x73\x74\x20\x67\x65\x74\x20\x73\x68\x65\x6c\x6c\x3a\x0a\x0a" 419 | 420 | <-------------------- read ------------------> 421 | ls 422 | 423 | <-------------- dangerous syscall------------> 424 | <-------------- dangerous syscall------------>exit 425 | 426 | r_2 = "\x6c\x73\x0a\x65\x78\x69\x74\x0a" 427 | ``` 428 | 429 | 430 | 431 | 432 | 433 | ## Test I0GAN 434 | 435 | server 436 | 437 | ``` 438 | socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./i0gan 439 | ``` 440 | 441 | 442 | 443 | attacker 444 | 445 | ``` 446 | nc 127.0.0.1 1234 447 | Test puts: 448 | Test write� 449 | Test read: 450 | asdfadsf 451 | Test gets: 452 | 453 | asdfasdf 454 | Test get shell: 455 | 456 | ``` 457 | 458 | 459 | 460 | server log 461 | 462 | ``` 463 | cat /tmp/.waf/14_37_22_5159c.log 464 | ``` 465 | 466 | 467 | 468 | ``` 469 | // Date: 2021-06-20 14:37:22 470 | // Mode: I0GAN 471 | // CTF AWD PWN WAF 472 | // Deved By I0gan 473 | 474 | <-------------------- write -----------------> 475 | Test puts: 476 | Test write� 477 | Test read: 478 | 479 | w_0 = "\x54\x65\x73\x74\x20\x70\x75\x74\x73\x3a\x0a\x54\x65\x73\x74\x20\x77\x72\x69\x74\x65\x00\x01\x02\x03\xff\x0a\x54\x65\x73\x74\x20\x72\x65\x61\x64\x3a\x0a" 480 | 481 | <-------------------- read ------------------> 482 | asdfadsf 483 | 484 | r_0 = "\x61\x73\x64\x66\x61\x64\x73\x66\x0a" 485 | 486 | <-------------------- write -----------------> 487 | Test gets: 488 | 489 | 490 | w_1 = "\x54\x65\x73\x74\x20\x67\x65\x74\x73\x3a\x0a\x0a" 491 | 492 | <-------------------- read ------------------> 493 | asdfasdf 494 | 495 | r_1 = "\x61\x73\x64\x66\x61\x73\x64\x66\x0a" 496 | 497 | <-------------------- write -----------------> 498 | Test get shell: 499 | 500 | 501 | <-------------- dangerous syscall------------> 502 | AVOID 503 | 504 | w_2 = "\x54\x65\x73\x74\x20\x67\x65\x74\x20\x73\x68\x65\x6c\x6c\x3a\x0a\x0a" 505 | ``` 506 | 507 | 508 | 509 | ## Test FORWARD 510 | 511 | victim's ip is 127.0.0.1, port is 20000 512 | 513 | test nc 514 | 515 | ``` 516 | nc 127.0.0.1 20000 517 | ____ _ _ ___ _____ 518 | / ___| | | |_ _|_ _| 519 | | | | | | || | | | 520 | | |___| |_| || | | | 521 | \____|\___/|___| |_| 522 | 523 | CUIT 2021 Experimental Class Exam [PWN 1 calc error] 524 | 525 | Why you wanna enter experimental class? 526 | 527 | asdf 528 | Ok, Now you can input two number to calc, Can you make an error? 529 | asdf 530 | Invalid! 531 | ``` 532 | 533 | 534 | 535 | In makefile 536 | 537 | ``` 538 | FORWARD_IP := 127.0.0.1 539 | FORWARD_PORT := 20000 540 | ``` 541 | 542 | Compile forward 543 | 544 | Server 545 | 546 | ``` 547 | socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./forward 548 | ``` 549 | 550 | attacker 551 | 552 | ``` 553 | nc 127.0.0.1 1234 554 | ____ _ _ ___ _____ 555 | / ___| | | |_ _|_ _| 556 | | | | | | || | | | 557 | | |___| |_| || | | | 558 | \____|\___/|___| |_| 559 | 560 | CUIT 2021 Experimental Class Exam [PWN 1 calc error] 561 | 562 | Why you wanna enter experimental class? 563 | 564 | asfasdfasdf 565 | Ok, Now you can input two number to calc, Can you make an error? 566 | asdf 567 | Invalid! 568 | 569 | ``` 570 | 571 | 572 | 573 | server log 574 | 575 | ``` 576 | cat /tmp/.waf/14_41_28_81a62.log 577 | ``` 578 | 579 | 580 | 581 | ``` 582 | // Date: 2021-06-20 14:41:28 583 | // Mode: FORWARD 584 | // CTF AWD PWN WAF 585 | // Deved By I0gan 586 | 587 | <-------------------- write -----------------> 588 | ____ _ _ ___ _____ 589 | / ___| | | |_ _|_ _| 590 | | | | | | || | | | 591 | | |___| |_| || | | | 592 | \____|\___/|___| |_| 593 | 594 | CUIT 2021 Experimental Class Exam [PWN 1 calc error] 595 | 596 | Why you wanna enter experimental class? 597 | 598 | 599 | w_0 = "\x20\x20\x5f\x5f\x5f\x5f\x20\x5f\x20\x20\x20\x5f\x20\x5f\x5f\x5f\x20\x5f\x5f\x5f\x5f\x5f\x0a\x20\x2f\x20\x5f\x5f\x5f\x7c\x20\x7c\x20\x7c\x20\x7c\x5f\x20\x5f\x7c\x5f\x20\x20\x20\x5f\x7c\x0a\x7c\x20\x7c\x20\x20\x20\x7c\x20\x7c\x20\x7c\x20\x7c\x7c\x20\x7c\x20\x20\x7c\x20\x7c\x20\x20\x0a\x7c\x20\x7c\x5f\x5f\x5f\x7c\x20\x7c\x5f\x7c\x20\x7c\x7c\x20\x7c\x20\x20\x7c\x20\x7c\x20\x20\x0a\x20\x5c\x5f\x5f\x5f\x5f\x7c\x5c\x5f\x5f\x5f\x2f\x7c\x5f\x5f\x5f\x7c\x20\x7c\x5f\x7c\x20\x0a\x0a\x43\x55\x49\x54\x20\x32\x30\x32\x31\x20\x45\x78\x70\x65\x72\x69\x6d\x65\x6e\x74\x61\x6c\x20\x43\x6c\x61\x73\x73\x20\x45\x78\x61\x6d\x20\x5b\x50\x57\x4e\x20\x31\x20\x63\x61\x6c\x63\x20\x65\x72\x72\x6f\x72\x5d\x0a\x0a\x57\x68\x79\x20\x79\x6f\x75\x20\x77\x61\x6e\x6e\x61\x20\x65\x6e\x74\x65\x72\x20\x65\x78\x70\x65\x72\x69\x6d\x65\x6e\x74\x61\x6c\x20\x63\x6c\x61\x73\x73\x3f\x0a\x0a" 600 | 601 | <-------------------- read ------------------> 602 | asfasdfasdf 603 | 604 | r_0 = "\x61\x73\x66\x61\x73\x64\x66\x61\x73\x64\x66\x0a" 605 | 606 | <-------------------- write -----------------> 607 | Ok, Now you can input two number to calc, Can you make an error? 608 | 609 | w_1 = "\x4f\x6b\x2c\x20\x4e\x6f\x77\x20\x79\x6f\x75\x20\x63\x61\x6e\x20\x69\x6e\x70\x75\x74\x20\x74\x77\x6f\x20\x6e\x75\x6d\x62\x65\x72\x20\x74\x6f\x20\x63\x61\x6c\x63\x2c\x20\x43\x61\x6e\x20\x79\x6f\x75\x20\x6d\x61\x6b\x65\x20\x61\x6e\x20\x65\x72\x72\x6f\x72\x3f\x0a" 610 | 611 | <-------------------- read ------------------> 612 | asdf 613 | 614 | r_1 = "\x61\x73\x64\x66\x0a" 615 | 616 | <-------------------- write -----------------> 617 | Invalid! 618 | 619 | w_2 = "\x49\x6e\x76\x61\x6c\x69\x64\x21\x0a" 620 | 621 | ``` 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | ## Test FORWARD_MULTI 630 | 631 | In hosts.txt 632 | 633 | ``` 634 | 127.0.0.1:20000 635 | 127.0.0.1:8081 636 | ``` 637 | 638 | The 8081 port is not open. 639 | 640 | Server 641 | 642 | ``` 643 | socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./forward_multi 644 | ``` 645 | 646 | 647 | 648 | Attacker: 649 | 650 | ``` 651 | [i0gan@arch ~]$ nc 127.0.0.1 1234 652 | ____ _ _ ___ _____ 653 | / ___| | | |_ _|_ _| 654 | | | | | | || | | | 655 | | |___| |_| || | | | 656 | \____|\___/|___| |_| 657 | 658 | CUIT 2021 Experimental Class Exam [PWN 1 calc error] 659 | 660 | Why you wanna enter experimental class? 661 | 662 | asdfasdf 663 | Ok, Now you can input two number to calc, Can you make an error? 664 | asdf 665 | Invalid! 666 | 667 | [i0gan@arch ~]$ nc 127.0.0.1 1234 668 | 127.0.0.1 669 | 670 | [i0gan@arch ~]$ nc 127.0.0.1 1234 671 | ____ _ _ ___ _____ 672 | / ___| | | |_ _|_ _| 673 | | | | | | || | | | 674 | | |___| |_| || | | | 675 | \____|\___/|___| |_| 676 | 677 | CUIT 2021 Experimental Class Exam [PWN 1 calc error] 678 | 679 | Why you wanna enter experimental class? 680 | 681 | asdfasdf 682 | Ok, Now you can input two number to calc, Can you make an error? 683 | asdf 684 | Invalid! 685 | ``` 686 | 687 | The server listener log 688 | 689 | ``` 690 | socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./forward_multi 691 | connect: Connection refused 692 | 2021/06/20 14:49:48 socat[31889] E waitpid(): child 31890 exited with status 111 693 | connect: Connection refused 694 | 2021/06/20 14:50:02 socat[31919] E waitpid(): child 31920 exited with status 111 695 | connect: Connection refused 696 | 2021/06/20 14:50:07 socat[31928] E waitpid(): child 31929 exited with status 111 697 | ``` 698 | 699 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | 2 | ## CTF AWD WAF FOR PWN 3 | 4 | github仓库地址:https://github.com/I0gan/pwn_waf 5 | 6 | 作者:I0gan 7 | 8 | 翻译:moon、i0gan 9 | 10 | 邮箱:i0gan@mail.pwnsky.com 11 | 12 | ### 简介 13 | 14 | PWN WAF是AWD PWN的流量抓包工具,方便分析和编写反攻击exp,非常有利于PWN Ctfer的使用。该工具已在 AWD 比赛中多次使用。给我一颗星是最大对我的支持。 WAF 有四种模式。 CATCH模式只是简单的捕获被攻击的交互流量,可以在日志路径下查看。 I0GAN模式是一种防御模式,可以防止攻击者拿到shell,也可以查看攻击者的交互流量。此模式使用谨慎,请遵守 ctf 竞赛规则。如果您违反了比赛规则。后果由你们自己承担。 FORWARD模式只是简单的转发攻击者的流量去打别人,我们可以在中间过程抓到攻击者的流量。当然,如果攻击者成功获取到flag,我们也可以在日志文件中获取到flag。 FORWARD_MUTIL 是基于 FORWARD 模式的,它主要从 hosts.txt 文件中循环获取受害者的主机信息,然后将攻击者的流量转发给受害者。 15 | 16 | 17 | 18 | pwn_waf是一套用于在AWD的PWN攻防中使用的流量捕获工具,便于在PWN对抗中进行分析和编写对应脚本。这套工具共有四个模块,或者说这个工具有4种模式。 19 | 20 | 1、CATCH 程序/模式,捕获模式,只捕获PWN被攻击时产生的交互流量,并保存在指定的日志文件夹(默认为/tmp/.waf/,可以通过在编译前修改makefile文件进行更改)里。日志格式命名规则为:攻击发生的时间(本地系统时间)+ 微妙的hex值 + .log后缀 21 | 2、I0GAN 程序/模式,防守模式,可以阻止攻击者通过PWN程序获取SHELL,还可以通过保存的日志查看攻击流量(当前版本,攻击被拦截后,日志文件内仅有“AVOID”标识,无法看到本次攻击的payload信息。) 22 | 3、FORWARD 程序/模式,单目标转发模式,将攻击者对自己的流量转发到指定地址的指定端口(需要在编译FORWAR程序时,修改makefile文件),如果攻击者获得了被转发目标的flag,可以在本地保存的日志中查看到。 23 | 4、FORWARD_MUTIL 程序/模式 多目标转发模式,基于forward程序,从hosts.txt文件(默认情况下,需要放置在日志文件夹里,格式为“IP:PORT”为一行)中循环读取被转发的目标,将攻击者流量转发至这些目标。 24 | 25 | ### 原理 26 | 27 | 捕获和防守模式: 28 | 29 | 通过创建一个子进程来执行被防护的pwn程序,父进程使用ptrace监控子进程的系统调用。如果调用了系统I/O接口,则读取对应的数据,并保存到日志文件中。如果发现了危险的系统调用,也会记录到日志文件中。 30 | 31 | 单目标及多目标转发模式: 32 | 33 | 从标准I/O接口捕获流量,转发至对应目标。可以通过日志文件查看攻击者和被转发目标之间的通信,还可以看到攻击者成功获取的flag信息。 34 | 35 | ### 配置说明 36 | 37 | 1、找到自己要防守的程序 38 | 登录自己要防守的机器,可以使用cat /etc/xinetd.d/pwn 命令查找(该方法并不通用),可以通过server字段找到pwn程序路径。 39 | 40 | ``` 41 | service pwn 42 | { 43 | disable = no 44 | socket_type = stream 45 | protocol = tcp 46 | wait = no 47 | user = pwn 48 | type = UNLISTED 49 | port = 80 50 | bind = 0.0.0.0 51 | server = /pwn 52 | server_args = none 53 | # safety options 54 | per_source = 10 # the maximum instances of this service per source IP address 55 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 56 | rlimit_as = 256M # the Address Space resource limit for the service 57 | #access_times = 8:50-17:10 58 | } 59 | ``` 60 | 61 | 62 | 63 | 如果你的结果回显如下,server字段显示为“chroot”程序,则需要查看“server_args”参数,在该示例中,要防守的程序路径为:/home/pwn/pwn 64 | 65 | ``` 66 | service pwn 67 | { 68 | disable = no 69 | flags = REUSE 70 | socket_type = stream 71 | protocol = tcp 72 | wait = no 73 | user = root 74 | type = UNLISTED 75 | port = 80 76 | 77 | bind = 0.0.0.0 78 | server = /usr/sbin/chroot 79 | server_args = --userspec=1000:1000 /home/pwn ./pwn 80 | # safety options 81 | per_source = 5 # the maximum instances of this service per source IP address 82 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 83 | rlimit_as = 100M # the Address Space resource limit for the service 84 | #access_times = 8:50-17:10 85 | } 86 | ``` 87 | 88 | 可以通过touch命令,核对找到的文件路径是否正确。 89 | 90 | 2、寻找一个当前用户拥有读写权限的目录,通常可以使用 /tmp 目录。如果寻找不到这类目录,则只能使用 转发模式。 91 | 92 | 3、修改配置文件,编译程序。打开makefile文件进行配置的修改。 93 | 94 | 捕获及防守模式: 95 | ``` 96 | # 配置 97 | # log path 98 | LOG_METHOD := OPEN # 是否生成log文件,如果找不到拥有写权限的目录,可以使用“CLOSE”进行关闭 99 | LOG_PATH := /tmp/.waf # 日志文件路径,注意使用找到的当前用户拥有读写权限的目录替换 100 | ARCH := 64 # 被防守程序是32位/64位的,(可以使用 file /pwn 查看) 101 | ``` 102 | 103 | 104 | 105 | 转发模式: 106 | 107 | 主要配置如下两个选项: 108 | 109 | ``` 110 | # Just used in FORWARD mode 111 | FORWARD_IP := 127.0.0.1 # 要将流量转发到的目标地址 112 | FORWARD_PORT := 20000 # 目标地址的业务端口地址,一般是将其转发到目标pwn程序监听的端口 113 | ``` 114 | 115 | 如果是多目标转发模式,需要将目标地址(IP:PORT)写入hosts.txt文件,放置在配置的文件夹里面。 116 | 117 | 118 | 119 | ### 编译 120 | 121 | 将四个模块全部编译生成: 122 | 123 | ``` 124 | make # or make all 125 | ``` 126 | 127 | 128 | 129 | 或者单独生成某个模块: 130 | 131 | ``` 132 | make catch 133 | make i0gan 134 | make forward 135 | make forward_multi 136 | ``` 137 | 138 | 139 | 140 | 141 | 142 | ### 运行使用 143 | 144 | 编译完成后,可以使用 ./catch ./i0gan 运行程序进行测试, 145 | 146 | 以/pwn 文件为要防守的程序,当前用户拥有/tmp 目录 为例: 147 | 将 catch/i0gan/forward/forward_multi 程序上传到/tmp 目录,如果要进行批量的转发,则将hosts.txt文件也上传。 148 | 149 | ``` 150 | mkdir /tmp/.waf # 创建一个目录,注意要和编译时的配置文件一致。 151 | chmod 777 /tmp/.waf # 修改对应的权限 152 | cp /pwn /tmp/.waf # 将要防护的pwn文件,复制到对应的目录 153 | cp /tmp/catch /pwn # 用catch 或者 i0gan 、 forward 替换原始的pwn文件 154 | cp /tmp/hosts.txt /tmp/.waf/hosts.txt # 将多目标防护的对象文件放到正确位置 155 | ``` 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /hosts.txt: -------------------------------------------------------------------------------- 1 | 1.12.220.15:10180 2 | 1.12.220.15:10280 3 | 1.12.220.15:10380 4 | 1.12.220.15:10480 5 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # Author: i0gan 2 | # Do : For compile waf program 3 | # Date : 2020-12-15 4 | 5 | GCC := gcc 6 | CFLAGS := -O2 --static 7 | RM := rm -rf 8 | 9 | # configure 10 | # log path 11 | LOG_METHOD := OPEN 12 | LOG_PATH := /tmp/.waf 13 | ARCH := 64 14 | 15 | # Just used in FORWARD mode 16 | FORWARD_IP := 127.0.0.1 17 | FORWARD_PORT := 20000 18 | 19 | # src 20 | TEST_PWN_SRC := src/test_pwn.c 21 | HEX_SRC := src/hex.c 22 | LOGGER_SRC := src/logger.c 23 | WAF_SRC := src/waf.c 24 | 25 | all : catch i0gan forward forward_multi test_pwn 26 | 27 | test_pwn : 28 | @mkdir $(LOG_PATH) 29 | $(GCC) $(TEST_PWN_SRC) -o $@ 30 | @cp $@ $(LOG_PATH)/pwn 31 | @cp hosts.txt $(LOG_PATH)/ 32 | 33 | clean : 34 | $(RM) catch i0gan forward forward_multi test_pwn $(LOG_PATH) 35 | 36 | catch : 37 | $(GCC) $(CFLAGS) $(WAF_SRC) $(LOGGER_SRC) -DRUN_MODE=CATCH -DLOG_METHOD=$(LOG_METHOD) -DLOG_PATH=\"$(LOG_PATH)\" -DARCH=$(ARCH) -o $@ 38 | 39 | i0gan : 40 | $(GCC) $(CFLAGS) $(WAF_SRC) $(LOGGER_SRC) -DRUN_MODE=I0GAN -DLOG_METHOD=$(LOG_METHOD) -DLOG_PATH=\"$(LOG_PATH)\" -DARCH=$(ARCH) -o $@ 41 | 42 | forward : 43 | $(GCC) $(CFLAGS) $(WAF_SRC) $(LOGGER_SRC) -DRUN_MODE=FORWARD -DLOG_METHOD=$(LOG_METHOD) -DLOG_PATH=\"$(LOG_PATH)\" -DARCH=$(ARCH) -DFORWARD_IP=\"$(FORWARD_IP)\" -DFORWARD_PORT=$(FORWARD_PORT) -o $@ 44 | 45 | forward_multi : 46 | $(GCC) $(CFLAGS) $(WAF_SRC) $(LOGGER_SRC) -DRUN_MODE=FORWARD_MULTI -DLOG_METHOD=$(LOG_METHOD) -DLOG_PATH=\"$(LOG_PATH)\" -DARCH=$(ARCH) -o $@ 47 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | // Author: i0gan 2 | // Github: https://github.com/i0gan/pwn_waf 3 | // Pwn Waf for AWD CTF 4 | 5 | #include "logger.h" 6 | 7 | struct log_buf logger = {NULL, 0, 0}; 8 | char logger_path[0x100] = {0}; 9 | int logger_fd = -1; 10 | 11 | void logger_init(const char *path) { 12 | // Create log dir 13 | //mkdir(LOG_PATH, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 14 | if (access(path, R_OK | W_OK) != 0) { 15 | mkdir(path, 0777); 16 | } 17 | strncpy(logger_path, path, 0x100); 18 | } 19 | 20 | int logger_open(const char *file_name) { 21 | logger_fd = open(file_name, O_CREAT | O_APPEND | O_WRONLY, 0666); 22 | if(logger_fd == -1) { 23 | perror("open:"); 24 | return 0; 25 | } 26 | return 1; 27 | } 28 | 29 | void logger_close() { 30 | close(logger_fd); 31 | free(logger.buf); 32 | logger.buf = NULL; 33 | } 34 | 35 | void logger_append_hex(const char *buf, int length) { 36 | char buf_[16] = {0}; 37 | unsigned char ch = 0; 38 | for(int i = 0; i < length; i++) { 39 | ch = (unsigned char)buf[i]; 40 | sprintf(buf_, "\\x%02x", ch); 41 | logger_append(buf_, strlen(buf_)); 42 | } 43 | } 44 | 45 | void logger_append(const char *buf, int length) { 46 | if(logger.cap == 0) { 47 | logger.buf = malloc(0x1000); 48 | logger.cap = 0x1000; 49 | } 50 | if(length >= (logger.cap - logger.size)) { 51 | int append_size = (length - (logger.cap - logger.size)) / 0x1000 + 0x1000; 52 | logger.buf = realloc(logger.buf, logger.cap + append_size); 53 | logger.cap += append_size; 54 | } 55 | memcpy(logger.buf + logger.size, buf, length); 56 | logger.size += length; 57 | } 58 | 59 | void logger_write_buf() { 60 | write(logger_fd, logger.buf, logger.size); 61 | logger.size = 0; 62 | } 63 | 64 | void logger_write(const char *buf, int len) { 65 | write(logger_fd, buf, len); 66 | } 67 | 68 | int logger_size() { 69 | return logger.size; 70 | } 71 | 72 | int logger_cap() { 73 | return logger.cap; 74 | } 75 | -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | // Author: i0gan 2 | // Github: https://github.com/i0gan/pwn_waf 3 | // Pwn Waf for AWD CTF 4 | 5 | # pragma once 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | struct log_buf { 13 | char *buf; 14 | int size; 15 | int cap; 16 | }; 17 | 18 | extern struct log_buf logger; 19 | extern char logger_path[0x100]; 20 | extern int logger_fd; 21 | 22 | extern void logger_init(const char *path); 23 | extern int logger_open(const char *file); 24 | extern void logger_close(); 25 | extern void logger_append_hex(const char *buf, int length); 26 | extern void logger_append(const char *buf, int length); 27 | extern void logger_write_buf(); 28 | extern void logger_append(const char *buf, int length); 29 | extern void logger_write_buf(); 30 | extern void logger_write(const char *buf, int len); 31 | extern int logger_size(); 32 | extern int logger_cap(); 33 | -------------------------------------------------------------------------------- /src/test_pwn.c: -------------------------------------------------------------------------------- 1 | // Author: i0gan 2 | // Github: https://github.com/i0gan/pwn_waf 3 | // Pwn Waf for AWD CTF 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) { 10 | setbuf(stdin, NULL); 11 | setbuf(stdout, NULL); 12 | char buf[128]; 13 | puts("Test puts:"); 14 | 15 | #define show_str "Test write\x00\x01\x02\x03\xff\n" 16 | write(1, show_str, sizeof(show_str) - 1); 17 | 18 | puts("Test read:"); 19 | read(0, buf, 128); 20 | 21 | puts("Test gets:\n"); 22 | gets(buf); 23 | 24 | puts("Test get shell:\n"); 25 | //system("/bin/sh"); 26 | 27 | execve("/bin/sh", NULL, NULL); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/waf.c: -------------------------------------------------------------------------------- 1 | // Author: i0gan 2 | // Github: https://github.com/i0gan/pwn_waf 3 | // Pwn Waf for AWD CTF 4 | 5 | #include "waf.h" 6 | #include 7 | #include 8 | #include 9 | 10 | const char logo_str[] = "// CTF AWD PWN WAF\n// Deved By I0gan\n"; 11 | const char read_str[] = "\n<-------------------- read ------------------>\n"; 12 | const char write_str[] = "\n<-------------------- write ----------------->\n"; 13 | const char dangerous_str[] = "\n<-------------- dangerous syscall------------>"; 14 | char *hosts_str1_buf = NULL; 15 | char *hosts_str2_buf = NULL; 16 | enum log_state waf_log_state = LOG_NONE_; 17 | 18 | int waf_run_mode = RUN_MODE; 19 | int waf_write_times = 0; 20 | int waf_read_times = 0; 21 | char send_buf[SEND_BUF_SIZE]; 22 | char recv_buf[SEND_BUF_SIZE]; 23 | 24 | void set_fd_nonblock(int fd) { 25 | int flags = fcntl(fd, F_GETFL, 0); 26 | flags |= O_NONBLOCK; 27 | fcntl(fd, F_SETFL,flags); 28 | } 29 | 30 | int readn(int fd, char *buf, int length) { 31 | int read_sum = 0; 32 | while(read_sum < length) { 33 | int read_size = read(fd, buf + read_sum, length - read_sum); 34 | if(read_size <= 0) 35 | break; 36 | read_sum += read_size; 37 | } 38 | return read_sum; 39 | } 40 | 41 | int writen(int fd, void *buffer, size_t length) { 42 | int write_left = length; 43 | int write_len = 0; 44 | int write_sum = 0; 45 | char *write_ptr = (char *)buffer; 46 | while(write_left > 0) { 47 | if((write_len = write(fd, write_ptr, write_left)) < 0) { 48 | if(errno == EINTR) 49 | continue; 50 | else if(errno == EAGAIN) { 51 | return write_sum; 52 | }else { 53 | return -1; 54 | } 55 | } 56 | write_sum += write_len; 57 | write_left -= write_len; 58 | write_ptr += write_len; 59 | } 60 | return write_sum; 61 | } 62 | 63 | void waf_write_logo() { 64 | #if LOG_METHOD == CLOSE 65 | return; 66 | #endif 67 | 68 | char time_str[128] = {0}; 69 | struct timeval tv; 70 | time_t time; 71 | gettimeofday(&tv, NULL); 72 | time = tv.tv_sec; 73 | struct tm *p_time = localtime(&time); 74 | strftime(time_str, 128, "// Date: %Y-%m-%d %H:%M:%S\n", p_time); 75 | logger_write(time_str, strlen(time_str)); 76 | if(waf_run_mode == CATCH) { 77 | logger_write(MODE_CATCH_STR, strlen(MODE_CATCH_STR)); 78 | }else if(waf_run_mode == I0GAN){ 79 | logger_write(MODE_I0GAN_STR, strlen(MODE_I0GAN_STR)); 80 | }else if(waf_run_mode == FORWARD) { 81 | logger_write(MODE_FORWARD_STR, strlen(MODE_FORWARD_STR)); 82 | }else if(waf_run_mode == FORWARD_MULTI) { 83 | logger_write(MODE_FORWARD_MULTI_STR, strlen(MODE_FORWARD_MULTI_STR)); 84 | }else {} 85 | logger_write(logo_str, sizeof(logo_str) - 1); 86 | } 87 | 88 | void waf_write_hex_log() { 89 | #if LOG_METHOD == CLOSE 90 | return; 91 | #endif 92 | if(logger_size() == 0) return; 93 | char str[0x60] = {0}; 94 | if(waf_log_state == LOG_WRITE_) { 95 | snprintf(str, 0x60, "\nw_%d = \"", waf_write_times); 96 | logger_write(str, strlen(str)); 97 | waf_write_times ++; 98 | }else { 99 | snprintf(str, 0x60, "\nr_%d = \"", waf_read_times); 100 | logger_write(str, strlen(str)); 101 | waf_read_times ++; 102 | } 103 | logger_write_buf(); 104 | logger_write("\"\n", 2); 105 | } 106 | 107 | void waf_log_open() { 108 | #if LOG_METHOD == CLOSE 109 | return; 110 | #endif 111 | char time_str[0x20] = {0}; 112 | char file_name[0x100] = {0}; 113 | struct timeval tv; 114 | time_t time_; 115 | gettimeofday(&tv, NULL); 116 | time_ = tv.tv_sec; 117 | struct tm *p_time = localtime(&time_); 118 | strftime(time_str, 128, "%H_%M_%S", p_time); 119 | snprintf(file_name, 0x100, "%s/%s_%lx%s", LOG_PATH, time_str, tv.tv_usec, ".log"); 120 | if(logger_open(file_name) == 0) { 121 | printf("Open log [%s] file failed!\n", file_name); 122 | exit(-1); 123 | } 124 | } 125 | 126 | // Write interactive log 127 | void waf_interactive_log(pid_t pid, char* addr, int size, enum log_state state) { 128 | int i = 0; 129 | char data; 130 | char* buf = (char*)malloc(size + 1); 131 | for(i = 0; i < size; i++){ 132 | data = ptrace(PTRACE_PEEKDATA, pid, addr + i, NULL); 133 | buf[i] = data; 134 | } 135 | if(waf_log_state != state) { 136 | // when state changed, then write data to log 137 | waf_write_hex_log(); // write_last_log_buf 138 | if(state == LOG_READ_) { 139 | logger_write(read_str, sizeof(read_str) - 1); 140 | } 141 | else { 142 | logger_write(write_str, sizeof(write_str) - 1); 143 | } 144 | waf_log_state = state; 145 | } 146 | logger_append_hex(buf, size); 147 | logger_write(buf, size); 148 | free(buf); 149 | } 150 | 151 | void bin_waf_run(int argc, char* argv[]) { 152 | pid_t pid; 153 | struct user_regs_struct regs; 154 | int status; 155 | int is_in_syscall = 0; 156 | int first_time = 1; 157 | int dangerous_syscall_times = 0; 158 | pid = fork(); 159 | int sys_num; 160 | enum log_state log_state_; 161 | // Use child process to exec 162 | if(pid == 0){ 163 | ptrace(PTRACE_TRACEME, 0, NULL, NULL); 164 | prctl(PR_SET_PDEATHSIG, SIGHUP); // when parent process exit, the child process also exit. 165 | argv[1] = LISTEN_ELF; 166 | status = execvp(LISTEN_ELF, argv + 1); 167 | //dup2(STDOUT_FILENO, STDERR_FILENO); 168 | if(status<0){ 169 | perror("exec"); 170 | return ; 171 | } 172 | } 173 | // parent to ptrace child syscall 174 | else if (pid > 0){ 175 | while(1) { 176 | wait(&status); 177 | if(WIFEXITED(status) || ERROR_EXIT(status)) 178 | break; 179 | // get rax to ensure witch syscall 180 | ptrace(PTRACE_GETREGS, pid, NULL, ®s); 181 | #if ARCH == 64 182 | sys_num = regs.orig_rax; 183 | #elif ARCH == 32 184 | sys_num = regs.orig_eax; 185 | #endif 186 | //printf("syscall %d\n", sys_num); 187 | if(DANGEROUS_SYSCALL(sys_num)) { 188 | dangerous_syscall_times += 1; 189 | if(dangerous_syscall_times > 1) { 190 | // dangerous syscall 191 | logger_write(dangerous_str, sizeof(dangerous_str) - 1); 192 | 193 | if(waf_run_mode == I0GAN) { 194 | // Write last data to log buf 195 | // Avoid input cmd from attacker 196 | //printf("syscall: %d\n", sys_num); 197 | logger_write("\nAVOID\n", sizeof("\nAVOID\n") - 1); 198 | //exit(0); 199 | return ; 200 | } 201 | } 202 | } 203 | if (sys_num != SYS_read && sys_num != SYS_write) { 204 | ptrace(PTRACE_SYSCALL, pid, NULL, NULL); 205 | continue; 206 | } 207 | if (is_in_syscall == 0) { 208 | is_in_syscall = 1; 209 | ptrace(PTRACE_SYSCALL, pid, 0, 0); 210 | } else { 211 | // Ignore the first time 212 | // Check it is standard pipe or not 213 | int is_standard_io = 0; 214 | #if ARCH == 64 215 | is_standard_io = STANDARD_IO(regs.rdi); 216 | #elif ARCH == 32 217 | is_standard_io = STANDARD_IO(regs.ebx); 218 | #endif 219 | if(!is_standard_io) { 220 | first_time = 0; 221 | ptrace(PTRACE_SYSCALL, pid, NULL ,NULL); 222 | is_in_syscall ^= 1; 223 | continue; 224 | } 225 | int size = 0; 226 | char* addr = NULL; 227 | int fd = -1; 228 | #if ARCH == 64 229 | addr = (char*)regs.rsi; 230 | fd = (int)regs.rdi; 231 | size = regs.rax; // readed length 232 | #elif ARCH == 32 233 | fd = regs.edx; 234 | addr = (char*)regs.ecx; 235 | size = regs.eax; // readed length 236 | #endif 237 | if(sys_num == SYS_read) { 238 | log_state_ = LOG_READ_; 239 | } 240 | else if (sys_num == SYS_write) { 241 | log_state_ = LOG_WRITE_; 242 | } 243 | waf_interactive_log(pid, addr, size, log_state_); 244 | is_in_syscall = 0; 245 | ptrace(PTRACE_SYSCALL, pid, NULL, NULL); 246 | } 247 | } 248 | } 249 | else{ 250 | perror("fork"); 251 | } 252 | 253 | } 254 | 255 | int connect_server(char* ip, ushort port) { 256 | struct sockaddr_in server_addr; 257 | int server_fd = -1; 258 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 259 | if(server_fd == -1) { 260 | perror("socket"); 261 | return -1; 262 | } 263 | bzero(&server_addr,sizeof(server_addr)); 264 | server_addr.sin_family = AF_INET; 265 | server_addr.sin_port = htons(port); 266 | if(inet_aton(ip, (struct in_addr*)&server_addr.sin_addr.s_addr) == 0){ 267 | perror("ip error"); 268 | return -1; 269 | } 270 | if(connect(server_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr)) == -1){ 271 | perror("connect"); 272 | exit(errno); 273 | } 274 | return server_fd; 275 | } 276 | 277 | void forward_waf_run() { 278 | fd_set read_fds, test_fds; 279 | int client_read_fd = 0; 280 | int client_write_fd = 1; 281 | int client_error_fd = 2; 282 | char *server_ip = NULL; 283 | ushort server_port = 0; 284 | 285 | #if RUN_MODE == FORWARD 286 | server_ip = FORWARD_IP; 287 | server_port = FORWARD_PORT; 288 | #elif RUN_MODE == FORWARD_MULTI 289 | int ret = get_host_from_file(&server_ip, &server_port); 290 | if(ret == -1) { 291 | return; 292 | } 293 | #endif 294 | int server_fd = connect_server(server_ip, server_port); 295 | 296 | FD_ZERO(&read_fds); 297 | FD_ZERO(&test_fds); 298 | 299 | FD_SET(server_fd, &read_fds); 300 | FD_SET(client_read_fd, &read_fds); // standard input fd 301 | FD_SET(client_write_fd, &read_fds); // standard write fd 302 | FD_SET(client_error_fd, &read_fds); // standard error fd 303 | 304 | set_fd_nonblock(server_fd); 305 | set_fd_nonblock(client_read_fd); 306 | set_fd_nonblock(client_write_fd); 307 | set_fd_nonblock(client_error_fd); 308 | 309 | while(1) { 310 | enum log_state log_state_ = LOG_NONE_; 311 | test_fds = read_fds; 312 | int result = select(FD_SETSIZE, &test_fds, (fd_set *)0, (fd_set *)0, (struct timeval *) 0); 313 | if(result < 1) { 314 | perror("select"); 315 | exit(errno); 316 | } 317 | for(int fd = 0; fd < FD_SETSIZE; fd ++) { 318 | if(FD_ISSET(fd,&test_fds)) { 319 | int write_size = -1; 320 | int read_size = -1; 321 | if(fd == server_fd) { 322 | write_size = read(server_fd, recv_buf, RECV_BUF_SIZE); 323 | writen(client_write_fd, recv_buf, write_size); 324 | }else if(fd == client_read_fd) { 325 | read_size = read(client_read_fd, send_buf, SEND_BUF_SIZE); 326 | writen(server_fd, send_buf, read_size); 327 | }else if(fd == client_write_fd) { 328 | read_size = read(client_write_fd, send_buf, SEND_BUF_SIZE); 329 | writen(server_fd, send_buf, read_size); 330 | }else if(fd == client_error_fd) { 331 | read_size = read(client_error_fd, send_buf, SEND_BUF_SIZE); 332 | writen(server_fd, send_buf, read_size); 333 | } 334 | 335 | // handle disconnect 336 | if(read_size == 0 || write_size == 0) { 337 | close(fd); 338 | free(hosts_str1_buf); 339 | free(hosts_str2_buf); 340 | return; 341 | } 342 | 343 | if(read_size > 0 || write_size > 0) { 344 | if(read_size > 0) 345 | log_state_ = LOG_READ_; 346 | else 347 | log_state_ = LOG_WRITE_; 348 | if(waf_log_state != log_state_) { 349 | // when state changed, then write data to log 350 | waf_write_hex_log(); // write_last_log_buf 351 | if(log_state_ == LOG_READ_) { 352 | logger_write(read_str, sizeof(read_str) - 1); 353 | } else { 354 | logger_write(write_str, sizeof(write_str) - 1); 355 | } 356 | waf_log_state = log_state_; 357 | } 358 | if(read_size > 0) { 359 | logger_append_hex(send_buf, read_size); 360 | logger_write(send_buf, read_size); 361 | } else { 362 | logger_append_hex(recv_buf, write_size); 363 | logger_write(recv_buf, write_size); 364 | } 365 | } 366 | } 367 | } 368 | } 369 | } 370 | 371 | void waf_init() { 372 | setbuf(stdin, NULL); 373 | setbuf(stdout, NULL); 374 | #if LOG_METHOD == OPEN 375 | logger_init(LOG_PATH); 376 | waf_log_open(); 377 | waf_write_logo(); 378 | #endif 379 | 380 | #if RUN_MODE == I0GAN 381 | prctl(PR_SET_PDEATHSIG, SIGHUP); 382 | #endif 383 | } 384 | 385 | int main(int argc, char *argv[]) { 386 | waf_init(); 387 | #if RUN_MODE == FORWARD 388 | forward_waf_run(); 389 | #elif RUN_MODE == FORWARD_MULTI 390 | forward_waf_run(); 391 | #elif RUN_MODE == I0GAN 392 | bin_waf_run(argc, argv); 393 | #elif RUN_MODE == CATCH 394 | bin_waf_run(argc, argv); 395 | #endif 396 | waf_write_hex_log(); 397 | logger_close(); 398 | return 0; 399 | } 400 | 401 | 402 | int get_host_from_file(char **ip, ushort *port) { 403 | int attack_index = 0; 404 | char attack_index_str[8] = {0}; 405 | int fd = open(HOSTS_FILE, O_RDONLY); 406 | int ai_fd = open(HOSTS_ATTACK_INDEX_FILE, O_RDWR | O_CREAT, 0666); 407 | 408 | read(ai_fd, attack_index_str, sizeof(attack_index_str)); 409 | attack_index = atoi(attack_index_str); 410 | if(attack_index == -1) attack_index = 0; 411 | //printf("now attack_index: %d, file %s ", attack_index, attack_index_str); 412 | 413 | if(fd == -1) { 414 | perror("open:"); 415 | return -1; 416 | } 417 | 418 | struct stat stat; 419 | int ret = fstat(fd, &stat); 420 | if(ret == -1) { 421 | perror("fstat:"); 422 | return -1; 423 | } 424 | char *hosts_str = malloc(stat.st_size + 0x10); 425 | char *hosts_str2 = malloc(stat.st_size + 0x10); 426 | read(fd, hosts_str, stat.st_size); 427 | memcpy(hosts_str2, hosts_str, stat.st_size); 428 | 429 | char *p = NULL; 430 | char *line = hosts_str; 431 | int line_nums = 0; 432 | 433 | p = strsep(&line, "\n"); 434 | while(p != NULL) { 435 | char *info = p; 436 | char *f_ip = strsep(&info, ":"); 437 | char *f_port = strsep(&info, ":"); 438 | if(f_ip != NULL && f_port != NULL) 439 | line_nums ++; 440 | p = strsep(&line, "\n"); 441 | } 442 | 443 | //printf("line_nums : %d\n", line_nums); 444 | line = hosts_str2; 445 | p = strsep(&line, "\n"); 446 | int now_index = 0; 447 | while(p != NULL) { 448 | char *info = p; 449 | char *f_ip = strsep(&info, ":"); 450 | char *f_port = strsep(&info, ":"); 451 | if(f_ip != NULL && f_port != NULL) { 452 | if(now_index == attack_index) { 453 | *ip = f_ip; 454 | *port = atoi(f_port); 455 | //printf("host: %s:%d\n", *ip, *port); 456 | break; 457 | } 458 | now_index ++; 459 | } 460 | p = strsep(&line, "\n"); 461 | //puts(hosts_str); 462 | } 463 | attack_index = (attack_index + 1) % line_nums; 464 | snprintf(attack_index_str, sizeof(attack_index_str), "%d", attack_index); 465 | lseek(ai_fd, 0, SEEK_SET); 466 | write(ai_fd, attack_index_str, sizeof(attack_index_str)); 467 | close(ai_fd); 468 | close(fd); 469 | return 0; 470 | } 471 | -------------------------------------------------------------------------------- /src/waf.h: -------------------------------------------------------------------------------- 1 | // Author: i0gan 2 | // Github: https://github.com/i0gan/pwn_waf 3 | // Pwn Waf for AWD CTF 4 | 5 | # pragma once 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "logger.h" 26 | 27 | #define CATCH 0x01 28 | #define I0GAN 0x02 29 | #define FORWARD 0x03 30 | #define FORWARD_MULTI 0x04 31 | 32 | // just used log method 33 | #define OPEN 0x01 34 | #define CLOSE 0x02 35 | 36 | #define LISTEN_ELF LOG_PATH "/pwn" // trace elf file 37 | #define HOSTS_FILE LOG_PATH "/hosts.txt" // hosts file for forward 38 | #define HOSTS_ATTACK_INDEX_FILE LOG_PATH "/.multi_index" // hosts attack index file for FORWARD_MULTI mode 39 | 40 | #define MODE_CATCH_STR "// Mode: CATCH\n" 41 | #define MODE_I0GAN_STR "// Mode: I0GAN\n" 42 | #define MODE_FORWARD_STR "// Mode: FORWARD\n" 43 | #define MODE_FORWARD_MULTI_STR "// Mode: FORWARD_MULTI\n" 44 | 45 | #define SEND_BUF_SIZE 0x2000 46 | #define RECV_BUF_SIZE 0x2000 47 | 48 | #ifndef FORWARD_IP 49 | #define FOWRARD_IP "127.0.0.1" 50 | #endif 51 | 52 | #ifndef FORWARD_PORT 53 | #define FORWARD_PORT 8080 54 | #endif 55 | 56 | #ifndef LOG_METHOD 57 | #define LOG_METHOD OPEN 58 | #endif 59 | 60 | enum log_state { 61 | LOG_NONE_, 62 | LOG_READ_, 63 | LOG_WRITE_ 64 | }; 65 | 66 | // error code avoid zombie parent process 67 | #define ERROR_EXIT(x) \ 68 | x == 0xb7f 69 | 70 | // judge is standard io 71 | #define STANDARD_IO(x) \ 72 | x == 0 || \ 73 | x == 1 || \ 74 | x == 2 75 | 76 | // dangerous syscall 77 | #define DANGEROUS_SYSCALL(x) \ 78 | x == __NR_open || \ 79 | x == __NR_clone || \ 80 | x == __NR_execve 81 | 82 | 83 | int readn(int fd, char *buf, int length); 84 | int writen(int fd, void *buffer, size_t length); 85 | void waf_write_logo(); 86 | void waf_write_hex_log(); 87 | void waf_log_open(); 88 | void waf_interactive_log(pid_t pid, char* addr, int size, enum log_state state); 89 | void bin_waf_run(int argc, char* argv[]); 90 | int connect_server(char* ip, ushort port); 91 | void forward_waf_run(); 92 | void waf_init(); 93 | int get_host_from_file(char **ip, ushort *port); 94 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./forward 3 | socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./i0gan 4 | --------------------------------------------------------------------------------