├── .github └── workflows │ └── docker.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── challenges ├── cyber-grand-challenge │ └── CROMU_00007 │ │ ├── Makefile │ │ ├── README.cgc.md │ │ ├── README.md │ │ ├── crash.input │ │ ├── include │ │ └── timecard.h │ │ ├── pov │ │ ├── POV_00000.xml │ │ ├── POV_00001.xml │ │ └── POV_00002.xml │ │ ├── sample.input │ │ └── src │ │ ├── service.c │ │ └── timecard.c ├── date │ ├── ANSWERS-libFuzzer.md │ ├── ANSWERS.md │ ├── HINTS.md │ ├── README.md │ └── coreutils-8.29-gnulib-fflush.patch ├── heartbleed │ ├── ANSWERS-libFuzzer.md │ ├── ANSWERS.md │ ├── HINTS.md │ ├── README.md │ ├── handshake.cc │ ├── server.key │ └── server.pem ├── libxml2 │ ├── ANSWERS-libFuzzer.md │ ├── ANSWERS.md │ ├── HINTS.md │ └── README.md ├── ntpq │ ├── ANSWERS-libFuzzer.md │ ├── ANSWERS.md │ ├── HINTS.md │ ├── README.md │ └── ntpq.dict └── sendmail │ ├── 1301 │ ├── ANSWERS-libFuzzer.md │ ├── ANSWERS.md │ ├── HINTS.md │ ├── Makefile │ ├── README.md │ ├── main.c │ ├── mime1-bad.c │ ├── mime1-bad.c.orig │ └── my-sendmail.h │ └── 1305 │ ├── ANSWERS.md │ ├── HINTS.md │ ├── Makefile │ ├── README.md │ ├── prescan-overflow-bad-fuzz.c │ ├── prescan-overflow-bad-start.c │ ├── prescan-overflow-bad.c │ └── prescan-overflow-bad.c.orig ├── environment ├── Dockerfile ├── README.md ├── entrypoint.sh └── self-serve │ ├── .golangci.yml │ ├── Dockerfile │ ├── favicon.ico │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── mvp.css ├── harness ├── README.md ├── library.c ├── library.h ├── overview.drawio └── overview.svg └── quickstart ├── Makefile ├── README.md ├── afl-screenshot.png ├── inputs ├── head └── u └── vulnerable.c /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | docker-student: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: Set up QEMU 15 | uses: docker/setup-qemu-action@v1 16 | - name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v1 18 | 19 | - name: Login to Container Registry 20 | uses: docker/login-action@v1 21 | with: 22 | registry: ghcr.io 23 | username: ${{ github.repository_owner }} 24 | password: ${{ secrets.CR_PAT }} 25 | 26 | - name: Build and push 27 | id: build_push 28 | uses: docker/build-push-action@v2 29 | with: 30 | context: environment 31 | push: true 32 | tags: ghcr.io/mykter/fuzz-training:latest 33 | 34 | - name: Image digest 35 | run: echo ${{ steps.build_push.outputs.digest }} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | in 2 | input 3 | out 4 | output 5 | .*.swp 6 | vulnerable 7 | 8 | challenges/ntpq/ntp-4* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "challenges/libxml/libxml2"] 2 | path = challenges/libxml2/libxml2 3 | url = https://gitlab.gnome.org/GNOME/libxml2.git 4 | [submodule "challenges/heartbleed/openssl"] 5 | path = challenges/heartbleed/openssl 6 | url = https://github.com/openssl/openssl.git 7 | [submodule "challenges/date/coreutils"] 8 | path = challenges/date/coreutils 9 | url = https://git.savannah.gnu.org/git/coreutils.git 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Original contents of this repository, including quickstart/, environment/, 2 | harness/ and all README/HINTS/ANSWERS files are licensed under: 3 | MIT License. 4 | 5 | Copyright 2016, 2017 Thales UK Ltd 6 | Copyright 2019 Michael Macnair 7 | Copyright 2020 Michael Macnair 8 | Copyright 2021 Thales 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining 11 | a copy of this software and associated documentation files (the 12 | "Software"), to deal in the Software without restriction, including 13 | without limitation the rights to use, copy, modify, merge, publish, 14 | distribute, sublicense, and/or sell copies of the Software, and to 15 | permit persons to whom the Software is furnished to do so, subject to 16 | the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be 19 | included in all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | Cyber grand challenge code used in this repo is licensed under: 30 | Copyright (c) 2014 Cromulence LLC 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in 40 | all copies or substantial portions of the Software. 41 | 42 | 43 | sendmail vulnerable code samples are licensed under: 44 | MIT Copyright Notice 45 | 46 | Copyright 2003 M.I.T. 47 | 48 | Permission is hereby granted, without written agreement or royalty fee, to use, 49 | copy, modify, and distribute this software and its documentation for any 50 | purpose, provided that the above copyright notice and the following three 51 | paragraphs appear in all copies of this software. 52 | 53 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 54 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 55 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 56 | SUCH DAMANGE. 57 | 58 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 59 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 60 | AND NON-INFRINGEMENT. 61 | 62 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 63 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 64 | 65 | The heartbleed harness is: 66 | Copyright 2016 Google Inc. All Rights Reserved. 67 | Licensed under the Apache License, Version 2.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fuzzing with AFL workshop 2 | 3 | Materials of the "Fuzzing with AFL" workshop by Michael Macnair (@michael_macnair). 4 | 5 | This workshop introduces fuzzing and how to make the most of using American Fuzzy Lop, a popular and powerful fuzzer, 6 | through a series of challenges where you rediscover real vulnerabilities in popular open source projects. 7 | 8 | The first public version of this workshop was presented at SteelCon 2017 and it was revised for each of BSides London 9 | 2019, BSides Bristol 2019, and GrayHat 2020 (most notable change in this revision was a switch to 10 | [afl++](https://github.com/AFLplusplus/AFLplusplus)). 11 | 12 | # Presentation 13 | 14 | Via 15 | [Google slides](https://docs.google.com/presentation/d/1Ap3eUIo4RrI_9GAGfn2Q0RKtBPNmGhI1DZlivdcFKPo) 16 | and [as a PDF](https://github.com/mykter/afl-training/files/5454345/Fuzzing.with.AFL.-.GrayHat.2020.pdf). There is extra 17 | information in the speaker notes. 18 | 19 | GrayHat published [a recording of a remote version of the workshop](https://www.youtube.com/watch?v=6YLz9IGAGLw) on 20 | YouTube - this was created for a real-time workshop audience, but you can follow along at your own pace as long as you 21 | don't mind skipping a few pauses and ignoring references to Discord. 22 | 23 | The presentation suggests when to attempt the different challenges in this repository, and the video provides a 24 | walk-through of `quickstart` and `harness`. 25 | 26 | # Pre-requisites 27 | 28 | - 3-4 hours (more to complete all the challenges) 29 | - Linux machine 30 | - Basic C and command line experience - ability to modify and compile C programs. 31 | - Docker, or the dependencies described in `quickstart`. 32 | 33 | # Contents 34 | 35 | - quickstart - Do this first! A tiny sample program to get started with fuzzing, including instructions on how to setup 36 | your machine. 37 | - harness - the basics of creating a test harness. Do this if you have any doubts about the "plumbing" between afl-fuzz 38 | and the target code. 39 | - challenges - a set of known-vulnerable programs with fuzzing hints 40 | - docker - Instructions and Dockerfile for preparing a suitable environment, and hosting it on GCP if you wish. A 41 | prebuilt image can be pulled from [ghcr.io/mykter/fuzz-training](https://ghcr.io/mykter/fuzz-training). 42 | 43 | See the other READMEs for more information. 44 | 45 | # Challenges 46 | 47 | Challenges, roughly in recommended order, with any specific aspects they cover: 48 | 49 | - libxml2 - an ideal target, using ASAN and persistent mode. 50 | - heartbleed - infamous bug, using ASAN. 51 | - sendmail/1301 - parallel fuzzing 52 | - ntpq - fuzzing a network client; coverage analysis and increasing coverage 53 | - date - fuzzing environment variable input 54 | - cyber-grand-challenge - an easy vuln and an example of a hard to find vuln using afl 55 | - sendmail/1305 - persistent mode difficulties 56 | 57 | The challenges have HINTS.md and ANSWERS.md files - these contain useful information about fuzzing different targets 58 | even if you're not going to attempt the challenge. 59 | 60 | Most of the challenges also have an ANSWERS-libFuzzer.md file, for if you want to try out using LLVM's libFuzzer. These 61 | are brief descriptions of the differences for libFuzzer, and should be read alongside the afl docs (.md files). 62 | 63 | All of the challenges use real vulnerabilities from open source projects (the CVEs are identified in the descriptions), 64 | with the exception of the Cyber Grand Challenge extract, which is a synthetic vulnerability. 65 | 66 | The chosen bugs are all fairly well isolated, and (except where noted) are very amenable to fuzzing. This means that you 67 | should be able to discover the bugs with a relatively small amount of compute time - these won't take core-days, most of 68 | them will take core-minutes. That said, fuzz testing is by definition a random process, so there's no guarantee how long 69 | it will take to find a particular bug, just a probability distribution. 70 | 71 | # Links 72 | 73 | - The afl [docs/](https://github.com/AFLplusplus/AFLplusplus/tree/stable/docs) directory 74 | - Ben Nagy’s “Finding Bugs in OS X using AFL” [video](https://vimeo.com/129701495) 75 | - The [afl-users mailing list](https://groups.google.com/forum/#!forum/afl-users) 76 | - The smart fuzzer revolution (talk on the future of fuzzing): [video](https://www.youtube.com/watch?v=g1E2Ce5cBhI) / 77 | [slides](https://docs.google.com/presentation/d/1FgcMRv_pwgOh1yL5y4GFsl1ozFwd6PMNGlMi2ONkGec/edit#slide=id.g13a9c1bce4_6_0) 78 | - A [categorized collection of recent fuzzing papers](https://github.com/wcventure/FuzzingPaper) (there are a lot!) 79 | - [The Fuzzing Book](https://www.fuzzingbook.org/) - broad coverage of fuzzing 80 | - [libFuzzer](http://llvm.org/docs/LibFuzzer.html) 81 | - [libFuzzer workshop](https://github.com/Dor1s/libfuzzer-workshop) 82 | - [libFuzzer tutorial](https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md) 83 | - [More challenges](https://github.com/antonio-morales/EkoParty_Advanced_Fuzzing_Workshop) from an EkoParty workshop 84 | - Introduction to [triaging crashes](https://trustfoundry.net/introduction-to-triaging-fuzzer-generated-crashes/) 85 | - Google's [ClusterFuzz](https://github.com/google/clusterfuzz) and Microsoft's 86 | [OneFuzz](https://github.com/microsoft/onefuzz) 87 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/Makefile: -------------------------------------------------------------------------------- 1 | POLLS_RELEASE_SEED= -802674260 2 | POLLS_TESTING_SEED= -690922790 3 | AUTHOR_ID = CROMU 4 | SERVICE_ID = 00007 5 | CFLAGS = -O0 -g -Werror -Wno-overlength-strings -Wno-packed 6 | 7 | POLLS_RELEASE_COUNT = 100 8 | POLLS_TESTING_COUNT = 900 9 | 10 | all: cromu_00007 11 | 12 | clean: 13 | rm -f cromu_00007 14 | 15 | cromu_00007: src/timecard.c src/service.c 16 | ${CC} ${CFLAGS} -o cromu_00007 src/timecard.c src/service.c -I ./include 17 | 18 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/README.cgc.md: -------------------------------------------------------------------------------- 1 | # CGC Challenge Binary Template 2 | 3 | ## Author Information 4 | 5 | "Debbie Nuttall" 6 | 7 | ### DARPA performer group 8 | Cromulence LLC (CROMU) 9 | 10 | ## Description 11 | This is a payroll system that allows registration of users, logging of hours worked, and querying the system for payroll information. 12 | 13 | ### Feature List 14 | The system works for up to 50 employees over one calendar year. It will allow registration of both exempt and non exempt employees who make up to $75/hour. The system will log both standard time and overtime based on a standard 40 hour work week. The system will calculate a weekly paycheck per employee including removal of required payroll taxes*. Various reports can be generated. *This is a simulation only, do not use this to pay your employees. 15 | 16 | Employees can be registered as exempt or non-exempt. Exempt employees are paid at the same hourly rate for overtime and standardtime. Non-exempt employees are paid time-and-a-half for overtime. For all employees anything over 8 hours on a weekday and all weekend time is treated as overtime. By default, employees are treated as non-exempt 17 | 18 | The following reports can be generated by the system: 19 | "all" - Show all payroll information for everyone. 20 | "one" - Show all payroll information for one employee. 21 | "week" - Show payroll information for one employee for a specific week. 22 | "week_all" - Show payroll information for all employees for a specific week. 23 | 24 | Input to the program is in the form of key-value pairs using the syntax: key-value` 25 | Some key-value pairs are required for certain commands and the rest can be included in an arbitrary order. Input will be processed as a single command when a newline is received. 26 | 27 | The program will continue to operate in a loop processing input and generating output until a blank line is entered to terminate the session. 28 | 29 | User registration can contain the following key-value pairs: 30 | Key Value 31 | employee_id A number from 0 to 49 32 | employee_name An ascii string 33 | wage The hourly wage of an employee (capped at 75.00) 34 | exempt Indicates an exempt employee. Values are 'yes' or 'no' 35 | 36 | Logging hours can contain the following key-value pairs: 37 | Key Value 38 | employee_id A number from 0 to 49 39 | week A number from 0 to 51 40 | monday Hours worked in the form #h#m 41 | ... hours are capped at 24 42 | sunday minutes are capped at 59 43 | 44 | A report query can contain the following key-value pairs: 45 | Key Value 46 | query Type of query [all|one|week|week_all] 47 | employee_id A number from 0 to 49 48 | week A number from 0 to 51 49 | 50 | For example, registering a non-exempt employee, logging one 40 hour work week, and generating a weekly payroll report would look like this: 51 | employee_id-1`employee_name-William Riker`wage-10.50` 52 | employee_id-1`week-1`monday-8h0m`tuesday-8h0m`wednesday-8h0m`thursday-8h0m`friday-8h0m` 53 | query-week`employee_id-1`week-1` 54 | 55 | 56 | ## Vulnerability 57 | There are two vulnerabilities in this program. The first vulnerability is in the process_query function in timecard.c. It allows an out of bounds read by using unverified user input as the index to an array when reading employee data. 58 | 59 | The second vulnerability is due to improper use of the payroll structure as defined in timecard.h and used incorrectly in the calculate_totalpay function in timecard.c. User input goes through a floating point calculation to affect the value of an integer which is then converted to an ascii decimal representation and written to a fixed length buffer. If the integer value becomes sufficiently large, the buffer will overflow into a nearby function pointer. Subsequent access to the function pointer results in limited EIP control. 60 | 61 | Information for all employees is stored in an array of employee structures located on the stack. Each structure contains an array of 52 payroll structures which store hours worked and paycheck information for each week of the year. 62 | 63 | When program input logs hours for an employee the hours are added to this payroll structure for the given week. Each time hours are logged, a payroll calculation occurs using floating point math to generate regular pay and overtime pay for that week. A structure containing integer values for dollars and cents is used to store the pay values after they are calculated. Because the payroll calculation must differ between exempt and non-exempt employees, there is a function pointer inside the payroll structure which points to the correct overtime calculation for that employee. This function pointer will be overwritten later on. 64 | 65 | When a report is generated via the query command, it calls the function calculate_totalpay. In order to display the pay amount, calculate_totalpay calls the function mtoa (short for money to ascii) to convert from integer values (for dollars and cents) to an ascii string. mtoa can write up to 19 characters total given the largest value for dollars. The vulnerability occurs because calculate_totalpay calls mtoa with an output buffer of only 12 bytes. The output buffer is located in an employee's payroll structure. 66 | 67 | If the pay amount is large enough (over 1,000,000 for one week), the buffer will overflow and a nearby function pointer is overwritten. The overwritten function pointer is calculate_overtime and as we previously discussed is used to calculate overtime for the employee. It will be called the next time hours are logged for that week for that employee. 68 | 69 | Hours for a single day are restricted to 24 or less and the pay rate is restricted to $75.00 or less, so a significant number of log entries are required to generate a pay amount large enough to overflow the buffer. In addition, for non-exempt employees the function pointer is re-initialized before it is called each time so the overflow will only lead to EIP control in cases where the employee was registered as exempt. 70 | 71 | In order to crash the program, the input must: 72 | 1. Register an employee as exempt. 73 | 2. Log sufficient hours to generate a one week pay amount of $1,000,000.00 or greater. 74 | 3. Generate a report for that employee's paycheck that week to cause the overflow. 75 | 4. Log additional hours for that employee that week to call the overwritten function pointer. 76 | 77 | ### Generic class of vulnerability 78 | Improper bounds check on an array dereference. 79 | Improper bounds check on an integer to ascii conversion allows overwrite of a function pointer. 80 | 81 | 82 | ### CWE classification 83 | CWE-118: Improper Access of Indexable Resource 84 | CWE-121: Stack-based Buffer Overflow 85 | 86 | 87 | ## Challenges 88 | 89 | The first vulnerability allows a simple user input to affect the location of a memory read. It was detected during internal testing using a fuzzer. 90 | 91 | The second vulnerability is not as straightforward. This CB allows input of many different key-value pairs in an arbitrary fashion. This will lead to a state explosion issue for symbolic program analysis. The large integer value needed for the overflow requires a significant number of key-value pairs in a particular order that is not informed by the structure of the CB. The vulnerable code lies after a floating point calculation potentially confusing systems which do not handle floating point. 92 | 93 | The code path needed to access the overwritten function pointer requires an additional key-value pair that is not present in the sample traffic given to the CRS. A successful fuzzing approach will require informing the fuzzer of the additional key-value pair and placing it in the correct location in input. 94 | 95 | 96 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/README.md: -------------------------------------------------------------------------------- 1 | This is an extract from the cyber grand challenge. README.cgc.md has a descriptions of the program and vulnerabilities. 2 | This version has been modified so that it runs on Linux not DECREE, the custom OS the challenge was designed for. 3 | 4 | There are two\* vulnerabilities in this program. The first is easily accessible. 5 | 6 | The second vulnerability looks like it is very hard to hit with fuzzing - crash.input has a sample crashing input for 7 | it. 8 | 9 | Have a go and let me know if my intuition is correct! 10 | 11 | There are no HINTS or ANSWERS files for this one - it is really straightforward to fuzz as the binary already takes 12 | input from stdin - refer to quickstart/harness/the other challenges for more details. And the second vuln is 13 | sufficiently hard that I don't have an answer for how to find it! 14 | 15 | You might want to fix the first vulnerability once you've found it so it's easier to notice if you catch the second. The 16 | source contains a patch - just tweak some ifdefs (but note if you define PATCHED, you'll also patch out the other 17 | vulnerability!). 18 | 19 | The second bug is definitely one for multicore; persistent mode (provided you can reset the state); and use of a 20 | dictionary (or a starting set of input files that contain all of the keywords). Quite possibly one for manual source 21 | code review instead of fuzzing. 22 | 23 | An interesting one to put into afl-analyse to see what it notices about the file format - it correctly annotates much of 24 | it, but misses on a few. `afl-analyze -i sample.input ./cromu_00007` 25 | 26 | \* unless I introduced more in porting to Linux 27 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/crash.input: -------------------------------------------------------------------------------- 1 | employee_id-1`employee_name-William Riker`wage-75`exempt-yes` 2 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 3 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 4 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 5 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 6 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 7 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 8 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 9 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 10 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 11 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 12 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 13 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 14 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 15 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 16 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 17 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 18 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 19 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 20 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 21 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 22 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 23 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 24 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 25 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 26 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 27 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 28 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 29 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 30 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 31 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 32 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 33 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 34 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 35 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 36 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 37 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 38 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 39 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 40 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 41 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 42 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 43 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 44 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 45 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 46 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 47 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 48 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 49 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 50 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 51 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 52 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 53 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 54 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 55 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 56 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 57 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 58 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 59 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 60 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 61 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 62 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 63 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 64 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 65 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 66 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 67 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 68 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 69 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 70 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 71 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 72 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 73 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 74 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 75 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 76 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 77 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 78 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 79 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 80 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 81 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 82 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 83 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 84 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 85 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 86 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 87 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 88 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 89 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 90 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 91 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 92 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 93 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 94 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 95 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 96 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 97 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 98 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 99 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 100 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 101 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 102 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 103 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 104 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 105 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 106 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 107 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 108 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 109 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 110 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 111 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 112 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 113 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 114 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 115 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 116 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 117 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m` 118 | query-week`employee_id-1`week-1` 119 | employee_id-1`week-1`friday-8h0m` 120 | 121 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/include/timecard.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Author: Debbie Nuttall 4 | 5 | Copyright (c) 2014 Cromulence LLC 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef TIMECARD_H 28 | #define TIMECARD_H 29 | 30 | #define WEEKS_IN_A_YEAR 52 31 | #define EMPLOYEE_NAME_LEN 64 32 | #define NUMBER_OF_EMPLOYEES 50 33 | #define PAYROLL_TAX_RATE .0765 34 | 35 | typedef struct money_s 36 | { 37 | int dollars; 38 | int cents; 39 | } money, *pmoney; 40 | 41 | typedef struct time_s 42 | { 43 | int hours; 44 | int minutes; 45 | } time, *ptime; 46 | 47 | // A function pointer will be used to calculate overtime differently for exempt vs non-exempt employees 48 | typedef void (*overtime_calc)(pmoney, pmoney, ptime); 49 | 50 | // A payroll struct is used to hold time and pay information for one week 51 | typedef struct payroll_s{ 52 | time standardtime; 53 | time overtime; 54 | money standardpay; 55 | money overtimepay; 56 | money payroll_tax; 57 | #ifdef PATCHED 58 | char paycheck[20]; 59 | #else 60 | char paycheck[12]; 61 | #endif 62 | overtime_calc calculate_overtime; 63 | } payroll, *ppayroll; 64 | 65 | // The employee structure holds various employee information as well as a payroll 66 | // record for each week of the year. 67 | typedef struct employee_s{ 68 | char name[EMPLOYEE_NAME_LEN]; 69 | int id; 70 | money wage; 71 | int exempt; 72 | payroll paychecks[WEEKS_IN_A_YEAR]; 73 | } employee, *pemployee; 74 | 75 | void atom(pmoney amount, char *str); 76 | void mtoa(char *str, pmoney amount); 77 | void atoh(ptime t, char *str); 78 | void htoa(char *str, ptime t); 79 | void initialize_employee(pemployee empl); 80 | void add_time(ptime t, int hours, int minutes); 81 | void add_money(pmoney dest, float money); 82 | void add_pay(pmoney pay, pmoney rate, ptime timeworked); 83 | void log_hours(ppayroll paycheck, char *hours); 84 | void log_overtime_hours(ppayroll paycheck, char *hours); 85 | void calculate_standardpay(pmoney pay, pmoney wage, ptime timeworked); 86 | void calculate_totalpay(ppayroll paycheck); 87 | void exempt_overtime(pmoney pay, pmoney wage, ptime timeworked); 88 | void nonexempt_overtime(pmoney pay, pmoney wage, ptime timeworked); 89 | int get_key_value(char *inbuf, size_t length, char **key, char **value); 90 | void process_key_value(pemployee empl, char *key, char *value, int *week); 91 | void merge_employee_records(pemployee empl, pemployee temp); 92 | void process_query(int query, employee employee_list[], pemployee temp, int week); 93 | void output_paycheck(pemployee empl, int week); 94 | 95 | int equals(char *a, char *b); 96 | 97 | // Read status codes 98 | #define READ_ERROR -1 99 | #define NEWLINE_RECEIVED 1 100 | #define KEY_VALUE_RECEIVED 2 101 | #define OTHER_INPUT_RECEIVED 3 102 | 103 | // Query codes 104 | #define QUERY_NONE 0 105 | #define QUERY_ONE 1 106 | #define QUERY_ALL 2 107 | #define QUERY_WEEK 3 108 | #define QUERY_WEEK_ALL 4 109 | 110 | #endif //TIMECARD_H 111 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/pov/POV_00000.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CROMU_00007 5 | 6 | 7 | busy_week 8 | 9 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m`saturday-24h59m`sunday-24h59m`\n 10 | 11 | 12 | 13 | employee_id-1`employee_name-Overpaid Employee`wage-75.00`exempt-yes`\n 14 | 15 | busy_week 16 | busy_week 17 | busy_week 18 | busy_week 19 | busy_week 20 | busy_week 21 | busy_week 22 | busy_week 23 | busy_week 24 | busy_week 25 | busy_week 26 | busy_week 27 | busy_week 28 | busy_week 29 | busy_week 30 | busy_week 31 | busy_week 32 | busy_week 33 | busy_week 34 | busy_week 35 | busy_week 36 | busy_week 37 | busy_week 38 | busy_week 39 | busy_week 40 | busy_week 41 | busy_week 42 | busy_week 43 | busy_week 44 | busy_week 45 | busy_week 46 | busy_week 47 | busy_week 48 | busy_week 49 | busy_week 50 | busy_week 51 | busy_week 52 | busy_week 53 | busy_week 54 | busy_week 55 | busy_week 56 | busy_week 57 | busy_week 58 | busy_week 59 | busy_week 60 | busy_week 61 | busy_week 62 | busy_week 63 | busy_week 64 | busy_week 65 | busy_week 66 | busy_week 67 | busy_week 68 | busy_week 69 | busy_week 70 | busy_week 71 | busy_week 72 | busy_week 73 | busy_week 74 | busy_week 75 | busy_week 76 | busy_week 77 | busy_week 78 | busy_week 79 | busy_week 80 | busy_week 81 | busy_week 82 | busy_week 83 | busy_week 84 | busy_week 85 | busy_week 86 | busy_week 87 | busy_week 88 | busy_week 89 | busy_week 90 | busy_week 91 | busy_week 92 | busy_week 93 | busy_week 94 | busy_week 95 | busy_week 96 | busy_week 97 | busy_week 98 | busy_week 99 | query-week`employee_id-1`week-1`\n 100 | employee_id-1`week-1`monday-1h0m`\n 101 | 102 | \n 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/pov/POV_00002.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CROMU_00007 5 | 6 | query-week`employee_id-100`\x0a\x0a 7 | \x0aExiting 8 | 9 | 10 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/sample.input: -------------------------------------------------------------------------------- 1 | employee_id-1`employee_name-William Riker`wage-75`exempt-yes` 2 | employee_id-1`week-1`monday-24h59m`tuesday-24h59m`wednesday-24h59m`thursday-24h59m`friday-24h59m`saturday-5h0m`sunday-3h32m` 3 | employee_id-2`employee_name-Michael Macnair`wage-5`exempt-no` 4 | employee_id-2`week-1`monday-4h59m`tuesday-4h00m`wednesday-2h39m`thursday-4h59m`friday-20h59m` 5 | query-week`employee_id-1`week-1` 6 | query-week_all`week-1` 7 | 8 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/src/service.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Author: Debbie Nuttall 4 | 5 | Copyright (c) 2014 Cromulence LLC 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include "timecard.h" 31 | 32 | int main(void) 33 | { 34 | int i; 35 | char inbuf[80]; 36 | char *key, *value; 37 | int status; 38 | employee temporary_record; 39 | int week; 40 | int blank_line = 1; 41 | int query = 0; 42 | // Store of all employee data 43 | employee employee_list[NUMBER_OF_EMPLOYEES]; 44 | 45 | for( i=0; i 0) 72 | { 73 | process_query(query, (employee *)&employee_list, &temporary_record, week); 74 | } 75 | else if ((temporary_record.id >= 0) && (temporary_record.id < NUMBER_OF_EMPLOYEES)) 76 | { 77 | merge_employee_records(&employee_list[temporary_record.id], &temporary_record); 78 | } 79 | // Get ready to process a new line 80 | initialize_employee(&temporary_record); 81 | week = 0; 82 | blank_line = 1; 83 | query = 0; 84 | break; 85 | case KEY_VALUE_RECEIVED: 86 | // If this is a query line remember it for processing later 87 | if ((blank_line == 1) && (equals(key, "query"))) 88 | { 89 | if (equals(value, "all")) 90 | query = QUERY_ALL; 91 | else if (equals(value, "one")) 92 | query = QUERY_ONE; 93 | else if (equals(value, "week")) 94 | query = QUERY_WEEK; 95 | else if (equals(value, "week_all")) 96 | query = QUERY_WEEK_ALL; 97 | else 98 | query = 0; 99 | } 100 | blank_line = 0; 101 | // Add the key_value information to a temporary record 102 | process_key_value(&temporary_record, key, value, &week); 103 | break; 104 | case OTHER_INPUT_RECEIVED: 105 | default: 106 | printf("ERROR: invalid input\n"); 107 | exit(1); 108 | break; 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /challenges/cyber-grand-challenge/CROMU_00007/src/timecard.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Author: Debbie Nuttall 4 | 5 | Copyright (c) 2014 Cromulence LLC 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "timecard.h" 33 | 34 | int equals(char *a, char *b) 35 | { 36 | return !strcmp(a,b); 37 | } 38 | 39 | // Converts a char string into a money struct. 40 | void atom(pmoney amount, char *str) 41 | { 42 | size_t i = 0; 43 | int negative = 0; 44 | amount->cents = 0; 45 | amount->dollars = 0; 46 | 47 | if(str[i] == '-') 48 | { 49 | negative = 1; 50 | i++; 51 | } 52 | for(; isdigit(str[i]); i++) 53 | { 54 | amount->dollars = (amount->dollars * 10) + (str[i] - '0'); 55 | } 56 | if (str[i] == '.') 57 | { 58 | if (isdigit(str[i+1])) 59 | { 60 | amount->cents = 10 * (str[i+1] - '0'); 61 | if (isdigit(str[i+2])) 62 | { 63 | amount->cents += (str[i+2] - '0'); 64 | } 65 | } 66 | } 67 | if (negative) 68 | { 69 | amount->dollars = -amount->dollars; 70 | } 71 | } 72 | 73 | // Converts a money struct into a character string 74 | // str should point to a buffer at least 19 bytes long to handle max value 75 | // $-2,147,483,647.00 76 | void mtoa(char *str, pmoney amount) 77 | { 78 | char buffer[20]; 79 | size_t pos = 0; 80 | size_t outpos; 81 | int value = amount->cents; 82 | int negative = 0; 83 | 84 | if (value < 0) 85 | { 86 | value = -value; 87 | } 88 | buffer[pos++] = '0' + (value % 10); 89 | value /= 10; 90 | buffer[pos++] = '0' + (value % 10); 91 | buffer[pos++] = '.'; 92 | value = amount->dollars; 93 | if (value < 0) 94 | { 95 | value = -value; 96 | negative = 1; 97 | } 98 | do 99 | { 100 | if ((pos == 6)||(pos == 10)||(pos==14)) 101 | { 102 | buffer[pos++] = ','; 103 | } 104 | buffer[pos++] = '0' + value % 10; 105 | value /= 10; 106 | } while((value > 0) && (pos < 63)); 107 | if (negative) 108 | { 109 | buffer[pos++] = '-'; 110 | } 111 | outpos = 1; 112 | str[0] = '$'; 113 | while(outpos <= pos) 114 | { 115 | str[outpos] = buffer[pos - outpos]; 116 | outpos++; 117 | } 118 | str[outpos] = '\0'; 119 | } 120 | 121 | // Converts a character string into a time struct 122 | void atoh(ptime t, char *str) 123 | { 124 | int pos = 0; 125 | int value = 0; 126 | t->hours = 0; 127 | t->minutes = 0; 128 | while(isdigit(str[pos])) 129 | { 130 | value = (value * 10) + (str[pos] - '0'); 131 | pos++; 132 | } 133 | if(str[pos] == 'h') 134 | { 135 | t->hours = value; 136 | pos++; 137 | } 138 | value = 0; 139 | while(isdigit(str[pos])) 140 | { 141 | value = (value * 10) + (str[pos] - '0'); 142 | pos++; 143 | } 144 | if (str[pos] == 'm') 145 | { 146 | t->minutes = value; 147 | } 148 | if (t->hours > 24) 149 | { 150 | t->hours = 24; 151 | } 152 | if (t->minutes > 59) 153 | { 154 | t->minutes = 59; 155 | } 156 | } 157 | 158 | 159 | // Converts a time struct into a character string 160 | // str should point to a buffer at least 15 bytes long to handle the max value 161 | // h2147483647m60\0 162 | void htoa(char *str, ptime t) 163 | { 164 | char buffer[15]; 165 | int pos=0; 166 | int outpos = 0; 167 | int value; 168 | value = t->minutes; 169 | if (value > 60) 170 | { 171 | str[outpos] = '\0'; 172 | return; 173 | } 174 | buffer[pos++] = 'm'; 175 | do 176 | { 177 | buffer[pos++] = '0' + value % 10; 178 | value /= 10; 179 | } while(value > 0); 180 | buffer[pos++] = 'h'; 181 | value = t->hours; 182 | do 183 | { 184 | buffer[pos++] = '0' + value % 10; 185 | value /= 10; 186 | } while(value > 0); 187 | while(outpos < pos) 188 | { 189 | str[outpos] = buffer[pos - outpos - 1]; 190 | outpos++; 191 | } 192 | str[outpos] = '\0'; 193 | } 194 | 195 | // Initialize an employee structure 196 | void initialize_employee(pemployee empl) 197 | { 198 | int i,j; 199 | empl->name[0] = '\0'; 200 | // An id value of -1 is used elsewhere to determine whether an employee has been registered 201 | empl->id = -1; 202 | empl->wage.dollars = 0; 203 | empl->wage.cents = 0; 204 | empl->exempt = 0; 205 | for (i=0; ipaychecks[i].standardtime.hours = 0; 208 | empl->paychecks[i].standardtime.minutes = 0; 209 | empl->paychecks[i].overtime.hours = 0; 210 | empl->paychecks[i].overtime.minutes = 0; 211 | empl->paychecks[i].calculate_overtime = &exempt_overtime; 212 | empl->paychecks[i].payroll_tax.dollars = 0; 213 | empl->paychecks[i].payroll_tax.cents = 0; 214 | memset(empl->paychecks[i].paycheck, 0, sizeof(empl->paychecks[i].paycheck)); 215 | } 216 | } 217 | 218 | // Adds hours and minutes to a time struct 219 | void add_time(ptime t, int hours, int minutes) 220 | { 221 | t->hours += hours + (t->minutes + minutes) / 60; 222 | t->minutes = (t->minutes + minutes) % 60; 223 | } 224 | 225 | // Rounds time to the nearest quarter hour 226 | void round_minutes(ptime t) 227 | { 228 | 229 | if (t->minutes < 8) 230 | { 231 | t->minutes = 0; 232 | } 233 | else if (t->minutes < 23) 234 | { 235 | t->minutes = 15; 236 | } 237 | else if (t->minutes < 38) 238 | { 239 | t->minutes = 30; 240 | } 241 | else if (t->minutes < 53) 242 | { 243 | t->minutes = 45; 244 | } 245 | else 246 | { 247 | t->hours += 1; 248 | t->minutes = 0; 249 | } 250 | } 251 | 252 | // Adds money to a money struct, rounding cents to nearest 1c. 253 | void add_money(pmoney dest, float money) 254 | { 255 | float total; 256 | // Convert dest amount back to float 257 | total = (float)dest->dollars + ((float)dest->cents / 100.0); 258 | // Add money and round up/down to nearest cent 259 | total = total + money; 260 | 261 | if (total > 0) 262 | { 263 | total = total + 0.005; 264 | } 265 | else 266 | { 267 | total = total - 0.005; 268 | } 269 | // Convert back to money struct 270 | dest->dollars = (int)total; 271 | dest->cents = (int)((total - (float)dest->dollars) * 100); 272 | 273 | } 274 | 275 | // Calculate pay and add to a money struct 276 | void add_pay(pmoney pay, pmoney rate, ptime timeworked) 277 | { 278 | float float_rate = (float)rate->dollars + ((float)rate->cents / 100.0); 279 | float minute_rate = float_rate / 60.0; 280 | 281 | add_money(pay, timeworked->hours * float_rate + timeworked->minutes * minute_rate); 282 | } 283 | 284 | // Adds time to an employee's payroll 285 | void log_hours(ppayroll paycheck, char *hours) 286 | { 287 | time t; 288 | atoh(&t, hours); 289 | // Round time worked to the nearest quarter hour 290 | round_minutes(&t); 291 | 292 | // Anything over 8 hours is considered overtime 293 | if (t.hours >= 8) 294 | { 295 | add_time(&paycheck->standardtime, 8, 0); 296 | add_time(&paycheck->overtime, t.hours - 8, t.minutes); 297 | } 298 | else 299 | { 300 | add_time(&paycheck->standardtime, t.hours, t.minutes); 301 | } 302 | } 303 | 304 | // Adds overtime to an employee's payroll (used for weekends) 305 | void log_overtime_hours(ppayroll paycheck, char *hours) 306 | { 307 | time t; 308 | atoh(&t, hours); 309 | // Round time worked to the nearest quarter hour 310 | round_minutes(&t); 311 | add_time(&paycheck->overtime, t.hours, t.minutes); 312 | } 313 | 314 | // Calculates standard pay based on wage and timeworked 315 | // Used to populate the standardpay field of an employee's weekly payroll 316 | void calculate_standardpay(pmoney pay, pmoney wage, ptime timeworked) 317 | { 318 | pay->dollars = 0; 319 | pay->cents = 0; 320 | 321 | add_pay(pay, wage, timeworked); 322 | } 323 | 324 | // Calculates total pay, subtracts taxes withheld, and outputs total to the 325 | // paycheck field of an employee's weekly payroll 326 | void calculate_totalpay(ppayroll paycheck) 327 | { 328 | money total; 329 | total.dollars = 0; 330 | total.cents = 0; 331 | add_money(&total, (float)paycheck->standardpay.dollars + (float)paycheck->standardpay.cents / 100.0); 332 | add_money(&total, (float)paycheck->overtimepay.dollars + (float)paycheck->overtimepay.cents / 100.0); 333 | add_money(&total, -1.0 * (((float)total.dollars + (float)total.cents/100.0) * PAYROLL_TAX_RATE) ); 334 | 335 | mtoa((char *)&paycheck->paycheck, &total); 336 | } 337 | 338 | // Calculates overtime pay for an exempt employee based on wage and overtime worked 339 | // Used to populate the overtimepay field of an employee's weekly payroll 340 | void exempt_overtime(pmoney pay, pmoney wage, ptime timeworked) 341 | { 342 | money overtime_wage; 343 | overtime_wage.dollars = 0; 344 | overtime_wage.cents = 0; 345 | pay->dollars = 0; 346 | pay->cents = 0; 347 | 348 | add_money(&overtime_wage, ((float)wage->dollars + ((float)wage->cents/100.0)) * 1.0); 349 | add_pay(pay, &overtime_wage, timeworked); 350 | } 351 | 352 | // Calculates overtime pay for an nonexempt employee based on wage and overtime worked 353 | // Used to populate the overtimepay field of an employee's weekly payroll 354 | void nonexempt_overtime(pmoney pay, pmoney rate, ptime timeworked) 355 | { 356 | pay->dollars = 0; 357 | pay->cents = 0; 358 | 359 | float float_rate = ((float)rate->dollars + ((float)rate->cents / 100.0)) * 1.5; 360 | float minute_rate = (float_rate / 60.0); 361 | add_money(pay, timeworked->hours * float_rate + timeworked->minutes * minute_rate ); 362 | } 363 | 364 | // Reads input from the network in the format key-value` and parses the 365 | // key/value pair by searching for delimiters and replacing with NULL 366 | // Returns status code: 367 | // READ_ERROR - Indicates a problem receiving from the network 368 | // NEWLINE_RECEIVED - Indicates a newline character was received 369 | // KEY_VALUE_RECEIVED - Indicates a valid key/value pair received 370 | // **key points to start of null terminated key string 371 | // **value points to start of null terminated value string 372 | // OTHER_INPUT_RECEIVED - Input received that does not match previous options 373 | int get_key_value(char *inbuf, size_t length, char **key, char **value) 374 | { 375 | int count; 376 | int pos = 0; 377 | char c; 378 | char buffer[80]; 379 | 380 | while ( (count = read(STDIN_FILENO, &c, 1)) == 1) { 381 | inbuf[pos++] = c; 382 | if(c == '\n') 383 | { 384 | return NEWLINE_RECEIVED; 385 | } else if(c == '`') { 386 | char *search = inbuf + pos - 1; 387 | *search = '\0'; 388 | while(search > inbuf) 389 | { 390 | search--; 391 | if (*search == '-') 392 | { 393 | *key = inbuf; 394 | *search = '\0'; 395 | *value = search + 1; 396 | return KEY_VALUE_RECEIVED; 397 | } 398 | } 399 | 400 | } 401 | } 402 | if (count != 0) { 403 | return READ_ERROR; 404 | } 405 | return OTHER_INPUT_RECEIVED; // EOF 406 | 407 | } 408 | 409 | // Checks the key/value pair for known keys. Parses values as necessary 410 | // to populate an employee structure. 411 | // The week field is used to maintain stateful awareness of the week when logging hours. 412 | // See merge_employee_records for proper usage of key/value pairs 413 | void process_key_value(pemployee empl, char *key, char *value, int *week) 414 | { 415 | if (equals(key, "employee_id")) 416 | { 417 | empl->id = atoi(value); 418 | } 419 | if (equals(key, "employee_name")) 420 | { 421 | strncpy(empl->name, value, EMPLOYEE_NAME_LEN); 422 | } 423 | if (equals(key, "wage")) 424 | { 425 | atom(&empl->wage, value); 426 | // Cap maximum wage at $75/hr 427 | if(empl->wage.dollars >= 75) 428 | { 429 | empl->wage.dollars = 75; 430 | empl->wage.cents = 0; 431 | } 432 | } 433 | if (equals(key, "exempt")) 434 | { 435 | if (equals(value, "yes")) 436 | { 437 | empl->exempt = 1; 438 | } 439 | } 440 | if (equals(key, "week")) 441 | { 442 | *week = atoi(value); 443 | if ((*week < 0) || (*week >= 52)) 444 | { 445 | *week = 0; 446 | } 447 | } 448 | if (equals(key, "monday") || 449 | equals(key, "tuesday") || 450 | equals(key, "wednesday") || 451 | equals(key, "thursday") || 452 | equals(key, "friday")) 453 | { 454 | log_hours(&empl->paychecks[*week], value); 455 | 456 | } 457 | if (equals(key, "saturday") || 458 | equals(key, "sunday")) 459 | { 460 | log_overtime_hours(&empl->paychecks[*week], value); 461 | } 462 | } 463 | 464 | // Merges the contents of one employee struct with another. 465 | // Used to add a line of input to the global employee record. 466 | // 467 | // When the line contains a name field, the line is treated as a 468 | // registration command which can have the following fields: 469 | // employee_id-#` 470 | // employee_name-ASCII` 471 | // wage-#.##` 472 | // exempt-(yes|no)` 473 | // Otherwise it is a time log which can have the following fields: 474 | // employee_id-#` 475 | // week-#` 476 | // monday-#h#m` 477 | // ... 478 | // sunday-#h#m` 479 | void merge_employee_records(pemployee empl, pemployee temp) 480 | { 481 | // Only populate employee record the first time a name is given 482 | if ((empl->id == -1) && (temp->name[0] != '\0')) 483 | { 484 | strncpy(empl->name, temp->name, EMPLOYEE_NAME_LEN); 485 | empl->id = temp->id; 486 | empl->wage.dollars = temp->wage.dollars; 487 | empl->wage.cents = temp->wage.cents; 488 | empl->exempt = temp->exempt; 489 | } 490 | // If employee does not yet exist and no name is given, ignore this line 491 | else if (empl->id == -1) 492 | { 493 | return; 494 | } 495 | // Otherwise, treat line as a time log 496 | else 497 | { 498 | int week; 499 | for (week = 0; week < WEEKS_IN_A_YEAR; week++) 500 | { 501 | add_time(&empl->paychecks[week].standardtime, temp->paychecks[week].standardtime.hours, 502 | temp->paychecks[week].standardtime.minutes); 503 | add_time(&empl->paychecks[week].overtime, temp->paychecks[week].overtime.hours, 504 | temp->paychecks[week].overtime.minutes); 505 | 506 | if (empl->exempt == 0) 507 | { 508 | empl->paychecks[week].calculate_overtime = &nonexempt_overtime; 509 | } 510 | 511 | calculate_standardpay(&empl->paychecks[week].standardpay, &empl->wage, &empl->paychecks[week].standardtime); 512 | empl->paychecks[week].calculate_overtime(&empl->paychecks[week].overtimepay, &empl->wage, &empl->paychecks[week].overtime); 513 | } 514 | } 515 | } 516 | 517 | // Process the query based on type 518 | // QUERY_ALL - outputs all valid employee records 519 | // QUERY_ONE - outputs one employee record 520 | // QUERY_WEEK - outputs one week of one employees record 521 | void process_query(int query, employee *employee_list, pemployee temp, int week) 522 | { 523 | switch (query) 524 | { 525 | case QUERY_ALL: 526 | { 527 | int employee, week; 528 | for (employee=0; employee < NUMBER_OF_EMPLOYEES; employee++) 529 | { 530 | // Ignore unregistered slots 531 | if (employee_list[employee].id == -1) 532 | continue; 533 | 534 | for (week=0; week < WEEKS_IN_A_YEAR; week++) 535 | { 536 | output_paycheck(&employee_list[employee], week); 537 | } 538 | 539 | } 540 | break; 541 | } 542 | case QUERY_ONE: 543 | { 544 | 545 | int week; 546 | if ((temp->id >= 0) && (temp->id < NUMBER_OF_EMPLOYEES)) 547 | { 548 | // Ignore unregistered slots 549 | if (employee_list[temp->id].id == -1) 550 | break; 551 | 552 | for (week = 0; week < WEEKS_IN_A_YEAR; week++) 553 | { 554 | output_paycheck(&employee_list[temp->id], week); 555 | } 556 | } 557 | break; 558 | } 559 | case QUERY_WEEK: 560 | #ifndef PATCHED 561 | // Ignore unregistered slots 562 | if (employee_list[temp->id].id == -1) 563 | break; 564 | #endif 565 | 566 | if ((temp->id >= 0) && (temp->id < NUMBER_OF_EMPLOYEES) 567 | && (week >= 0) && (week < WEEKS_IN_A_YEAR)) 568 | { 569 | #ifdef PATCHED 570 | // Ignore unregistered slots 571 | if (employee_list[temp->id].id == -1) 572 | break; 573 | #endif 574 | output_paycheck(&employee_list[temp->id], week); 575 | } 576 | break; 577 | case QUERY_WEEK_ALL: 578 | { 579 | int employee; 580 | if ((week >= 0) && (week < WEEKS_IN_A_YEAR)) 581 | { 582 | for (employee=0; employeename); 607 | printf("`week-%d", week); 608 | printf("`standardtime-"); 609 | htoa((char *)&outbuf, &empl->paychecks[week].standardtime); 610 | printf("%s",outbuf); 611 | printf("`overtime-"); 612 | htoa((char *)&outbuf, &empl->paychecks[week].overtime); 613 | printf("%s",outbuf); 614 | printf("`standardpay-"); 615 | mtoa((char *)&outbuf, &empl->paychecks[week].standardpay); 616 | printf("%s",outbuf); 617 | printf("`overtimepay-"); 618 | mtoa((char *)&outbuf, &empl->paychecks[week].overtimepay); 619 | printf("%s",outbuf); 620 | printf("`netpay-"); 621 | calculate_totalpay(&empl->paychecks[week]); 622 | printf("%s",empl->paychecks[week].paycheck); 623 | printf("`\n"); 624 | } 625 | 626 | 627 | // These functions are to be used by the poller to aleviate issues with floating point implementation differences 628 | // and rounding errors 629 | 630 | float c_standardpay(int hours, int minutes, int dollars, int cents) 631 | { 632 | money total; 633 | float float_rate = (float)dollars + ((float)cents / 100.0); 634 | float minute_rate = (float_rate / 60.0); 635 | total.dollars = 0; 636 | total.cents = 0; 637 | add_money(&total, hours * float_rate + minutes * minute_rate); 638 | return (float)total.dollars + ((float)total.cents / 100.0); 639 | } 640 | 641 | float c_overtimepay(int hours, int minutes, int dollars, int cents, int exempt) 642 | { 643 | money total; 644 | float float_rate; 645 | float minute_rate; 646 | if (exempt == 0) 647 | { 648 | float_rate = ((float)dollars + ((float)cents / 100.0)) * 1.5; 649 | } 650 | else 651 | { 652 | float_rate = (float)dollars + ((float)cents / 100.0); ; 653 | } 654 | total.dollars = 0; 655 | total.cents = 0; 656 | minute_rate = (float_rate / 60.0); 657 | add_money(&total, hours * float_rate + minutes * minute_rate); 658 | return (float)total.dollars + ((float)total.cents / 100.0); 659 | } 660 | 661 | float c_netpay(int hours, int minutes, int overhours, int overminutes, int dollars, int cents, int exempt) 662 | { 663 | money total, rate, standard, overtime; 664 | time standardtime, overtimetime; 665 | total.dollars = 0; 666 | total.cents = 0; 667 | rate.dollars = dollars; 668 | rate.cents = cents; 669 | standardtime.hours = hours; 670 | standardtime.minutes = minutes; 671 | calculate_standardpay(&standard, &rate, &standardtime); 672 | overtimetime.hours = overhours; 673 | overtimetime.minutes = overminutes; 674 | if (exempt == 0) 675 | nonexempt_overtime(&overtime, &rate, &overtimetime); 676 | else 677 | exempt_overtime(&overtime, &rate, &overtimetime); 678 | add_money(&total, (float)standard.dollars + (float)standard.cents/100.0); 679 | add_money(&total, (float)overtime.dollars + (float)overtime.cents/100.0); 680 | add_money(&total, -1.0 * (((float)total.dollars + (float)total.cents/100.0)*PAYROLL_TAX_RATE)); 681 | return (float)total.dollars + ((float)total.cents / 100.0); 682 | } 683 | 684 | -------------------------------------------------------------------------------- /challenges/date/ANSWERS-libFuzzer.md: -------------------------------------------------------------------------------- 1 | # Compiling 2 | 3 | You can't compile the whole program with `-fsanitize=fuzzer`, because the configure script will fail to pass its sanity 4 | check. Instead we configure with `-fsanitize=fuzzer-no-link` but then manually override that when it comes to actually 5 | compiling `date`. 6 | 7 | 1. Follow the instructions in README.md, but using these compiler options: 8 | `CC=clang CFLAGS="-fsanitize=address,fuzzer-no-link -g"` 9 | 10 | 2. Rebuild the date binary, using libFuzzer's main: 11 | `rm src/date src/date.o && make src/date CFLAGS="-g -fsanitize=address,fuzzer -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"` 12 | (note if you haven't replaced the existing main function yet, this will fail to link as there will be two mains) 13 | 14 | # Running 15 | 16 | This version of date leaks memory (which isn't hugely important given that date terminates immediately after doing its 17 | job), but ASAN will detect that and complain. Tell it not to: `src/date -detect_leaks=0 in/` 18 | 19 | # Harness 20 | 21 | Here is one way to modify `date.c` to have libFuzzer invoke the original main function with a fixed "commandline" and TZ 22 | set based on the fuzzer's input. 23 | 24 | As this target uses getopt, and we care about the commandline used, there is global state we have to manage to ensure 25 | getopt parses the commandline each time. 26 | 27 | We're using ifdefs so we can compile the target both in its original form and for fuzzing. 28 | 29 | You could also replace all references to stdout with a reference to /dev/null - see the ntpq's ANSWERS-libFuzzer.md for 30 | an example of this. 31 | 32 | ```c 33 | int 34 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 35 | origmain(int argc, char **argv) 36 | #else 37 | main(int argc, char **argv) 38 | #endif 39 | 40 | ... 41 | 42 | int LLVMFuzzerTestOneInput(uint8_t *data, size_t len) 43 | { 44 | char *tz = calloc(1, len + 1); 45 | memcpy(tz, data, len); 46 | setenv("TZ", tz, 1); 47 | 48 | char *argv[] = {"date", "--date=2017-03-14 15:00 UTC", NULL}; 49 | optind = 1; // getopt is used, which has global state that needs to be reset 50 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 51 | origmain(2, argv); 52 | #else 53 | // this will never get called, but it demonstrates a good practice of keeping your fuzz harnesses building alongside the normal application 54 | main(2,argv); 55 | #endif 56 | free(tz); 57 | return 0; 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /challenges/date/ANSWERS.md: -------------------------------------------------------------------------------- 1 | This is CVE-2017-7476. The fix and vulnerability are described here: 2 | http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=commit;h=94e01571507835ff59dd8ce2a0b56a4b566965a4 3 | 4 | To test that your built version has the vulnerability and is compiled with ASAN, run the POC: 5 | 6 | TZ="aaa00000000000000000000aaaaaab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ./src/date --date "2017-03-14 15:00 UTC" 7 | 8 | (alter as needed if you've already patched date and the TZ value is coming from stdin - in this case note there is no 9 | trailing newline!) 10 | 11 | From the options in HINTS: 12 | 13 | 1. This could work, but involves some potentially messy digging around in the code. Are you _sure_ you got every 14 | instance? Does your code work if it's retrieved multiple times? 15 | 2. In afl's utils/bash_shellshock directory there is a trivial patch for bash that uses this approach. It isn't 16 | bash-specific - you just make a call to `setenv` - so is easily adapted to this scenario. This is the simplest and 17 | most robust method, so probably the most suitable for this task. You can insert the same code at the start of 18 | `src/date.c`'s main function, but change the env var it is setting to TZ. 19 | 3. Creating a getenv replacement that is loaded via LD_PRELOAD might be useful work that you could reuse for other 20 | targets. But it's more effort than you need right now. 21 | 22 | A suitable fuzzing command on a 64 bit machine might be: 23 | 24 | $ echo -n "Europe/London" > in/london 25 | 26 | (note the "-n" to suppress a trailing new line) 27 | 28 | For vanilla fuzzing: 29 | 30 | $ afl-fuzz -i in -o out -- ./coreutils/src/date --date "2017-03-14 15:00 UTC" 31 | 32 | Note the fixed date, but with a timezone specified to ensure we don't skip past any timezone processing code. (this bug 33 | is triggered whether or not you specify a timezone in the date - but consider how this could affect what is tested!) 34 | 35 | For ASAN fuzzing: 36 | 37 | $ sudo ~/AFLplusplus/utils/asan_cgroups/limit_memory.sh -u fuzzer ~/AFLplusplus/afl-fuzz -i in -o out -- ./coreutils/src/date --date "2017-03-14T15:00-UTC" 38 | 39 | Note the format of the date - when called via the `limit_memory.sh` script the quotes get lost - you can either escape 40 | them or use a date format without spaces. 41 | 42 | (If you're having trouble with ASAN, you can try being lazy and just running without the cgroup-based memory limiter. 43 | The kernel might start killing off your processes if you hit an OOM condition, but in this instance it will probably be 44 | ok...) 45 | 46 | If your ASAN output in the crash doesn't give line numbers just memory addresses, check you've followed the quickstart 47 | README and setup llvm-symbolizer prior to compiling. 48 | -------------------------------------------------------------------------------- /challenges/date/HINTS.md: -------------------------------------------------------------------------------- 1 | The environment variable in question is TZ: 2 | 3 | $ ./src/date 4 | Mon Jul 3 08:11:23 PDT 2017 5 | $ TZ='Asia/Tokyo' ./src/date 6 | Tue Jul 4 00:12:34 JST 2017 7 | 8 | So, how to fuzz an environment variable? AFL doesn't have support for it built in. A few options: 9 | 10 | 1. Find all instances in the source code where the TZ environment variable is read, and replace it with reading from 11 | stdin. 12 | 2. Write a harness that sets the environment variable, then continues as normal (e.g. modify date.c's main function). 13 | 3. Use LD_PRELOAD to replace calls to getenv with a custom wrapper that picks up the value from stdin. 14 | 15 | Consider the pros and cons of each, and pick whichever suits! 16 | 17 | (In this particular case, as the manpage highlights, you can also pass TZ in the command line. Follow that route if 18 | you'd rather.) 19 | 20 | Notice that the default behaviour of date is to print out the current time - this might interfere with afl's 21 | determination of whether a particular input led to a change in execution path. Perhaps you could force it to output a 22 | fixed time. 23 | 24 | This bug can manifest itself with or without ASAN. ASAN will make triaging it easier and increases the likelyhood of 25 | detection, but you could run your crashes or queue on an ASAN compiled version of it separately, if you want. 26 | 27 | For fuzzing with ASAN: `$ AFL_USE_ASAN=1 make -j` and then refer to docs/notes_for_asan.txt 28 | 29 | This target works with afl-clang-lto, with the customary long link time. 30 | -------------------------------------------------------------------------------- /challenges/date/README.md: -------------------------------------------------------------------------------- 1 | This bug in gnulib was recently found using AFL against the coreutils date program. 2 | 3 | Check out the date manpage - it takes input from the command line, date syscall, environment variables, and optionally 4 | files. 5 | 6 | Here we want to fuzz an environment variable - refer to the manpage to identify it. 7 | 8 | The basic process for compiling date with afl is familiar. 9 | 10 | If you aren't in the workshop container environment, grab coreutils and some build dependencies: 11 | 12 | ```shell 13 | # not required in the workshop environment 14 | $ git submodule init && git submodule update 15 | $ sudo apt install autopoint bison gperf autoconf 16 | ``` 17 | 18 | Building this old version of coreutils is quite fragile - take care to follow these instructions precisely! 19 | 20 | ```shell 21 | $ cd coreutils 22 | $ ./bootstrap # may finish with some errors to do with 'po' files, which can be ignored 23 | 24 | # this old version doesn't work with modern compilers, we need to apply a patch 25 | # this patch can get overwritten by certain make targets - if you get an error during compilation about fseeko.c, try re-applying the patch 26 | $ patch --follow-symlinks -p1 < ../coreutils-8.29-gnulib-fflush.patch 27 | 28 | $ ./configure 29 | $ make # this will build everything. you can try `make src/date`, but the makefile doesn't specify all of the dependencies properly so this will probably fail until you've built everything once 30 | 31 | $ ./src/date 32 | Mon Jul 3 08:11:23 PDT 2017 33 | ``` 34 | -------------------------------------------------------------------------------- /challenges/date/coreutils-8.29-gnulib-fflush.patch: -------------------------------------------------------------------------------- 1 | From https://src.fedoraproject.org/rpms/coreutils/raw/f28/f/coreutils-8.29-gnulib-fflush.patch 2 | 3 | From 08d69db2f3c0e8506a1d126dd4dcdd0f14071161 Mon Sep 17 00:00:00 2001 4 | From: Paul Eggert 5 | Date: Mon, 5 Mar 2018 10:56:29 -0800 6 | Subject: [PATCH] fflush: adjust to glibc 2.28 libio.h removal 7 | MIME-Version: 1.0 8 | Content-Type: text/plain; charset=UTF-8 9 | Content-Transfer-Encoding: 8bit 10 | 11 | Problem reported by Daniel P. Berrangé in: 12 | https://lists.gnu.org/r/bug-gnulib/2018-03/msg00000.html 13 | * lib/fflush.c (clear_ungetc_buffer_preserving_position) 14 | (disable_seek_optimization, rpl_fflush): 15 | * lib/fpending.c (__fpending): 16 | * lib/fpurge.c (fpurge): 17 | * lib/freadahead.c (freadahead): 18 | * lib/freading.c (freading): 19 | * lib/freadptr.c (freadptr): 20 | * lib/freadseek.c (freadptrinc): 21 | * lib/fseeko.c (fseeko): 22 | * lib/fseterr.c (fseterr): 23 | * lib/stdio-impl.h (_IO_IN_BACKUP) [_IO_EOF_SEEN]: 24 | Define if not already defined. 25 | 26 | Upstream-commit: 4af4a4a71827c0bc5e0ec67af23edef4f15cee8e 27 | Signed-off-by: Kamil Dudka 28 | --- 29 | lib/fflush.c | 6 +++--- 30 | lib/fpending.c | 2 +- 31 | lib/fpurge.c | 2 +- 32 | lib/freadahead.c | 2 +- 33 | lib/freading.c | 2 +- 34 | lib/freadptr.c | 2 +- 35 | lib/freadseek.c | 2 +- 36 | lib/fseeko.c | 4 ++-- 37 | lib/fseterr.c | 2 +- 38 | lib/stdio-impl.h | 6 ++++++ 39 | 10 files changed, 18 insertions(+), 12 deletions(-) 40 | 41 | diff --git a/lib/fflush.c b/lib/fflush.c 42 | index 4e65692..c16da5f 100644 43 | --- a/lib/fflush.c 44 | +++ b/lib/fflush.c 45 | @@ -33,7 +33,7 @@ 46 | #undef fflush 47 | 48 | 49 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 50 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 51 | 52 | /* Clear the stream's ungetc buffer, preserving the value of ftello (fp). */ 53 | static void 54 | @@ -72,7 +72,7 @@ clear_ungetc_buffer (FILE *fp) 55 | 56 | #endif 57 | 58 | -#if ! (defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */) 59 | +#if ! (defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */) 60 | 61 | # if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT 62 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ 63 | @@ -148,7 +148,7 @@ rpl_fflush (FILE *stream) 64 | if (stream == NULL || ! freading (stream)) 65 | return fflush (stream); 66 | 67 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 68 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 69 | 70 | clear_ungetc_buffer_preserving_position (stream); 71 | 72 | diff --git a/lib/fpending.c b/lib/fpending.c 73 | index 5811a4a..9e21a16 100644 74 | --- a/lib/fpending.c 75 | +++ b/lib/fpending.c 76 | @@ -32,7 +32,7 @@ __fpending (FILE *fp) 77 | /* Most systems provide FILE as a struct and the necessary bitmask in 78 | , because they need it for implementing getc() and putc() as 79 | fast macros. */ 80 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 81 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 82 | return fp->_IO_write_ptr - fp->_IO_write_base; 83 | #elif defined __sferror || defined __DragonFly__ || defined __ANDROID__ 84 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ 85 | diff --git a/lib/fpurge.c b/lib/fpurge.c 86 | index 408b8fc..3a16000 100644 87 | --- a/lib/fpurge.c 88 | +++ b/lib/fpurge.c 89 | @@ -62,7 +62,7 @@ fpurge (FILE *fp) 90 | /* Most systems provide FILE as a struct and the necessary bitmask in 91 | , because they need it for implementing getc() and putc() as 92 | fast macros. */ 93 | -# if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 94 | +# if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 95 | fp->_IO_read_end = fp->_IO_read_ptr; 96 | fp->_IO_write_ptr = fp->_IO_write_base; 97 | /* Avoid memory leak when there is an active ungetc buffer. */ 98 | diff --git a/lib/freadahead.c b/lib/freadahead.c 99 | index f335f04..e7cb77b 100644 100 | --- a/lib/freadahead.c 101 | +++ b/lib/freadahead.c 102 | @@ -30,7 +30,7 @@ extern size_t __sreadahead (FILE *); 103 | size_t 104 | freadahead (FILE *fp) 105 | { 106 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 107 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 108 | if (fp->_IO_write_ptr > fp->_IO_write_base) 109 | return 0; 110 | return (fp->_IO_read_end - fp->_IO_read_ptr) 111 | diff --git a/lib/freading.c b/lib/freading.c 112 | index 78140d2..c9d3344 100644 113 | --- a/lib/freading.c 114 | +++ b/lib/freading.c 115 | @@ -31,7 +31,7 @@ freading (FILE *fp) 116 | /* Most systems provide FILE as a struct and the necessary bitmask in 117 | , because they need it for implementing getc() and putc() as 118 | fast macros. */ 119 | -# if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 120 | +# if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 121 | return ((fp->_flags & _IO_NO_WRITES) != 0 122 | || ((fp->_flags & (_IO_NO_READS | _IO_CURRENTLY_PUTTING)) == 0 123 | && fp->_IO_read_base != NULL)); 124 | diff --git a/lib/freadptr.c b/lib/freadptr.c 125 | index e4cc0b0..aba8dd5 100644 126 | --- a/lib/freadptr.c 127 | +++ b/lib/freadptr.c 128 | @@ -29,7 +29,7 @@ freadptr (FILE *fp, size_t *sizep) 129 | size_t size; 130 | 131 | /* Keep this code in sync with freadahead! */ 132 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 133 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 134 | if (fp->_IO_write_ptr > fp->_IO_write_base) 135 | return NULL; 136 | size = fp->_IO_read_end - fp->_IO_read_ptr; 137 | diff --git a/lib/freadseek.c b/lib/freadseek.c 138 | index fcecba6..98726f8 100644 139 | --- a/lib/freadseek.c 140 | +++ b/lib/freadseek.c 141 | @@ -36,7 +36,7 @@ freadptrinc (FILE *fp, size_t increment) 142 | /* Keep this code in sync with freadptr! */ 143 | #if HAVE___FREADPTRINC /* musl libc */ 144 | __freadptrinc (fp, increment); 145 | -#elif defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 146 | +#elif defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 147 | fp->_IO_read_ptr += increment; 148 | #elif defined __sferror || defined __DragonFly__ || defined __ANDROID__ 149 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ 150 | diff --git a/lib/fseeko.c b/lib/fseeko.c 151 | index d0f24d8..0ae2b15 100644 152 | --- a/lib/fseeko.c 153 | +++ b/lib/fseeko.c 154 | @@ -47,7 +47,7 @@ fseeko (FILE *fp, off_t offset, int whence) 155 | #endif 156 | 157 | /* These tests are based on fpurge.c. */ 158 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 159 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 160 | if (fp->_IO_read_end == fp->_IO_read_ptr 161 | && fp->_IO_write_ptr == fp->_IO_write_base 162 | && fp->_IO_save_base == NULL) 163 | @@ -123,7 +123,7 @@ fseeko (FILE *fp, off_t offset, int whence) 164 | return -1; 165 | } 166 | 167 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 168 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 169 | fp->_flags &= ~_IO_EOF_SEEN; 170 | fp->_offset = pos; 171 | #elif defined __sferror || defined __DragonFly__ || defined __ANDROID__ 172 | diff --git a/lib/fseterr.c b/lib/fseterr.c 173 | index 739e545..d998619 100644 174 | --- a/lib/fseterr.c 175 | +++ b/lib/fseterr.c 176 | @@ -29,7 +29,7 @@ fseterr (FILE *fp) 177 | /* Most systems provide FILE as a struct and the necessary bitmask in 178 | , because they need it for implementing getc() and putc() as 179 | fast macros. */ 180 | -#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 181 | +#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */ 182 | fp->_flags |= _IO_ERR_SEEN; 183 | #elif defined __sferror || defined __DragonFly__ || defined __ANDROID__ 184 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ 185 | diff --git a/lib/stdio-impl.h b/lib/stdio-impl.h 186 | index 329801a..eeaabab 100644 187 | --- a/lib/stdio-impl.h 188 | +++ b/lib/stdio-impl.h 189 | @@ -18,6 +18,12 @@ 190 | the same implementation of stdio extension API, except that some fields 191 | have different naming conventions, or their access requires some casts. */ 192 | 193 | +/* Glibc 2.28 made _IO_IN_BACKUP private. For now, work around this 194 | + problem by defining it ourselves. FIXME: Do not rely on glibc 195 | + internals. */ 196 | +#if !defined _IO_IN_BACKUP && defined _IO_EOF_SEEN 197 | +# define _IO_IN_BACKUP 0x100 198 | +#endif 199 | 200 | /* BSD stdio derived implementations. */ 201 | 202 | -- 203 | 2.16.2 204 | 205 | -------------------------------------------------------------------------------- /challenges/heartbleed/ANSWERS-libFuzzer.md: -------------------------------------------------------------------------------- 1 | This file describes the changes from the approach to AFL. Read the AFL README/HINTS/ANSWERS files first. 2 | 3 | # Compiling (README) 4 | 5 | To compile for libFuzzer, use: 6 | 7 | ```shell 8 | CC="clang -fsanitize=fuzzer-no-link,address" ./config 9 | make clean && make 10 | ``` 11 | 12 | `-fsanitizer=fuzzer-no-link`: this compiles the library with the fuzzer's instrumentation, but doesn't attempt to link 13 | in libFuzzer's `main`. 14 | 15 | Now compile your harness: 16 | 17 | ```shell 18 | clang -g -O2 -fsanitize=fuzzer,address libfuzzer-handshake.cc openssl/libssl.a openssl/libcrypto.a -o handshake-libfuzzer -I openssl/include 19 | ``` 20 | 21 | # Running 22 | 23 | ```shell 24 | ./handshake-libfuzzer 25 | ``` 26 | 27 | You should get a heap buffer overflow in the tls1_process_heartbeat function in a few seconds. 28 | 29 | # Harness 30 | 31 | The existing handshake.cc code is almost a libFuzzer harness already (this isn't a happy accident - it comes from a 32 | fuzzing test repo!). Just rename main to `extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)`, we 33 | need the extern C bit as we're compiling with a C++ compiler. 34 | -------------------------------------------------------------------------------- /challenges/heartbleed/ANSWERS.md: -------------------------------------------------------------------------------- 1 | An example way of getting data into sinbio, using stdin and some deferred forkserver goodness. 2 | 3 | Insert this immediately prior to the BIO_write: 4 | 5 | #ifdef __AFL_HAVE_MANUAL_CONTROL 6 | __AFL_INIT(); 7 | #endif 8 | 9 | uint8_t data[100] = {0}; 10 | size_t size = read(STDIN_FILENO, data, 100); 11 | if (size == -1) { 12 | printf("Failed to read from stdin\n"); 13 | return(-1); 14 | } 15 | 16 | Note the rate at which it finds new paths isn't astronomical, but that's OK - give it a few minutes tops and you should 17 | find success. 18 | 19 | When you find a crash, stop AFL and run the crash through the fuzzer. You should see in the ASAN stacktrace that the 20 | crash is in a heartbeat function - that's heartbleed! 21 | -------------------------------------------------------------------------------- /challenges/heartbleed/HINTS.md: -------------------------------------------------------------------------------- 1 | Because we're using ASAN, we need to run this in a slightly different way, see docs/notes_for_asan.txt: 2 | 3 | sudo ~/AFLplusplus/examples/asan_cgroups/limit_memory.sh -u fuzzer afl-fuzz -i in -o out ./handshake 4 | 5 | An alternative is to not use the limit_memory script. afl-fuzz defaults to using `-m none`, so this will work, but it 6 | runs the risk of the target allocating a huge amount of memory, and Linux will then start killing processes underneath 7 | you. 8 | -------------------------------------------------------------------------------- /challenges/heartbleed/README.md: -------------------------------------------------------------------------------- 1 | This is adapted from the libFuzzer example here: https://github.com/google/fuzzer-test-suite/tree/master/openssl-1.0.1f 2 | 3 | - Get the openSSL source for version OpenSSL_1_0_1f: 4 | 5 | git submodule init git submodule update 6 | 7 | - Configure and build with ASAN: 8 | 9 | cd openssl 10 | CC=afl-clang-fast CXX=afl-clang-fast++ ./config -d 11 | AFL_USE_ASAN=1 make 12 | 13 | (note you can do "make -j" for faster builds, but there is a race that makes this fail occasionally) 14 | 15 | (This target doesn't work with afl-clang-lto(++) at the time of writing) 16 | 17 | Now fix up the code in handshake.cc to work with afl. (or copy it out of ANSWERS.md!) 18 | 19 | Build our target: 20 | 21 | AFL_USE_ASAN=1 afl-clang-fast++ -g handshake.cc openssl/libssl.a openssl/libcrypto.a -o handshake -I openssl/include -ldl 22 | 23 | Pre-emptive hint: 24 | 25 | - Don't worry about seeds. This is easy to find without any. 26 | - (You've already got the hint to use Address Sanitizer in the build commands above, but have a think about heartbleed 27 | and why it's important we use ASAN) 28 | - Check out afl's docs/notes_for_asan.txt 29 | -------------------------------------------------------------------------------- /challenges/heartbleed/handshake.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef CERT_PATH 11 | # define CERT_PATH 12 | #endif 13 | 14 | SSL_CTX *Init() { 15 | SSL_library_init(); 16 | SSL_load_error_strings(); 17 | ERR_load_BIO_strings(); 18 | OpenSSL_add_all_algorithms(); 19 | SSL_CTX *sctx; 20 | assert (sctx = SSL_CTX_new(TLSv1_method())); 21 | /* These two file were created with this command: 22 | openssl req -x509 -newkey rsa:512 -keyout server.key \ 23 | -out server.pem -days 9999 -nodes -subj /CN=a/ 24 | */ 25 | assert(SSL_CTX_use_certificate_file(sctx, "server.pem", 26 | SSL_FILETYPE_PEM)); 27 | assert(SSL_CTX_use_PrivateKey_file(sctx, "server.key", 28 | SSL_FILETYPE_PEM)); 29 | return sctx; 30 | } 31 | 32 | int main() { 33 | static SSL_CTX *sctx = Init(); 34 | SSL *server = SSL_new(sctx); 35 | BIO *sinbio = BIO_new(BIO_s_mem()); 36 | BIO *soutbio = BIO_new(BIO_s_mem()); 37 | SSL_set_bio(server, sinbio, soutbio); 38 | SSL_set_accept_state(server); 39 | 40 | /* TODO: To spoof one end of the handshake, we need to write data to sinbio 41 | * here */ 42 | BIO_write(sinbio, data, size); 43 | 44 | SSL_do_handshake(server); 45 | SSL_free(server); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /challenges/heartbleed/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA1AdZNDVOA9cXm97f 3 | erp1bukz2kohjToJS6Ma8fOb36VV9lQGmDNsJanXFiqafOgV+kh1HXqZ3l1I0JmZ 4 | 71b+QQIDAQABAkAHGfPn5r0lLcgRpWZQwvv56f+dmQwEoeP7z4uwfNtEo0JcRD66 5 | 1WRCvx3LE0VbNeaEdNmSPiRXhlwIggjfrBi9AiEA9UusPBcEp/QcPGs96nQQdQzE 6 | fw4x0HL/eSV3qHimT6MCIQDdSAiX4Ouxoiwn/9KhDMcZXRYX/OPzj6w8u1YIH7BI 7 | ywIgSozbJdAhHCJ2ym4VfUIVFl3xAmSAA0hQGLOocE1qzl0CIQDRicOxZmhqBiKA 8 | IgznOn1StEYWov+MhRFZVSBLgw5gbwIgJzOlSlu0Y22hEUsLCKyHBrCAZZHcZ020 9 | 20pfogmQYn0= 10 | -----END PRIVATE KEY----- 11 | -------------------------------------------------------------------------------- /challenges/heartbleed/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBYTCCAQugAwIBAgIJAMPQQtUHkx+KMA0GCSqGSIb3DQEBCwUAMAwxCjAIBgNV 3 | BAMMAWEwHhcNMTYwOTI0MjIyMDUyWhcNNDQwMjA5MjIyMDUyWjAMMQowCAYDVQQD 4 | DAFhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANQHWTQ1TgPXF5ve33q6dW7pM9pK 5 | IY06CUujGvHzm9+lVfZUBpgzbCWp1xYqmnzoFfpIdR16md5dSNCZme9W/kECAwEA 6 | AaNQME4wHQYDVR0OBBYEFCXtEo9rkLuKGSlm0mFE4Yk/HDJVMB8GA1UdIwQYMBaA 7 | FCXtEo9rkLuKGSlm0mFE4Yk/HDJVMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL 8 | BQADQQCnldOnbdNJZxBO/J+979Urg8qDp8MnlN0979AmK1P5/YzPnAF4BU7QTOTE 9 | imS5qZ0MvziBa81nVlnnFRkIezcD 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /challenges/libxml2/ANSWERS-libFuzzer.md: -------------------------------------------------------------------------------- 1 | This file describes the changes from the approach to AFL. Read the AFL README/HINTS/ANSWERS files first. 2 | 3 | # Compiling (README) 4 | 5 | To compile libxml2 for libFuzzer, use: 6 | 7 | ```shell 8 | CC=clang CFLAGS=-fsanitize=fuzzer-no-link,address ./autogen.sh 9 | make -j4 10 | ``` 11 | 12 | Note the fuzzer-no-link: this compiles the library with the right instrumentation, but doesn't attempt to link in 13 | libFuzzer's `main`. 14 | 15 | Now compile your harness: 16 | 17 | ```shell 18 | clang -g -O2 -fsanitize=fuzzer,address ./libfuzzer-harness.c -I libxml2/include/ libxml2/.libs/libxml2.a -lz -lm -o libxml2-libfuzzer 19 | ``` 20 | 21 | # Running (HINTS) 22 | 23 | ```shell 24 | ./libxml2-libfuzzer -max_len=64 -dict=/home/fuzzer/AFLplusplus/dictionaries/xml.dict 25 | ``` 26 | 27 | Note we're specifying a pretty small limit on the input size - 64 bytes is enough to exercise a lot of XML 28 | functionality, and the smaller the input the faster it will run. We're also cheating: we happen to know this bug doesn't 29 | need a larger input to trigger it. 30 | 31 | You should get a result in a few minutes. 32 | 33 | You can go multi-core by specifying `-jobs=2` in the fuzzer invocation, for example. To track progress, try 34 | `tail -f fuzz-0.log`. 35 | 36 | # Harness (ANSWERS) 37 | 38 | This is probably the smallest harness you'll ever write. (You can make it smaller still by removing the error handling 39 | part, but that doesn't play nicely with libFuzzer, as it will clobber the output and slow down execution.) 40 | 41 | ```c 42 | #include 43 | #include 44 | #include 45 | 46 | void quietError(void *ctx, const char *msg, ...) {} 47 | 48 | int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) 49 | { 50 | xmlSetGenericErrorFunc(NULL, &quietError); // suppress all error output 51 | xmlDocPtr doc = xmlReadMemory((const char *)Data, Size, "noname.xml", NULL, 0); 52 | if (doc != NULL) 53 | { 54 | xmlFreeDoc(doc); 55 | } 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /challenges/libxml2/ANSWERS.md: -------------------------------------------------------------------------------- 1 | There are two options to help AFL out with fuzzing XML: using a good seed corpus and/or using a dictionary. Both options 2 | would provide the fuzzer with a comprehensive set of tokens that have special meaning to XML-consuming software. 3 | 4 | Happily AFL ships with a ready made XML dictionary, so we can use that: 5 | 6 | afl-fuzz -i in -o out -x ~/AFLplusplus/dictionaries/xml.dict ./fuzzer @@ 7 | 8 | You should see the numbers of paths found grow much faster using this approach. Crucially, we'll also uncover a bug that 9 | would never be found without it. 10 | 11 | Here is a complete example of a harness using persistent mode. By lifting the parser initializaiton and cleanup outside 12 | the loop you get a moderate speed-up at the cost of a slight stability-loss. We can drop the init/cleanup altogether, 13 | but that has a significant (7%) stability impact. 14 | 15 | ```c 16 | #include "libxml/parser.h" 17 | #include "libxml/tree.h" 18 | 19 | int main(int argc, char **argv) { 20 | if (argc != 2){ 21 | return(1); 22 | } 23 | 24 | xmlInitParser(); 25 | while (__AFL_LOOP(1000)) { 26 | xmlDocPtr doc = xmlReadFile(argv[1], NULL, 0); 27 | if (doc != NULL) { 28 | xmlFreeDoc(doc); 29 | } 30 | } 31 | xmlCleanupParser(); 32 | 33 | return(0); 34 | } 35 | ``` 36 | 37 | The small size of this wrapper (and the fact that it's all you need to find real bugs) reinforces some of the properties 38 | described in the README: libxml2 is very amenable to fuzzing. 39 | 40 | Here's another harness, this time utilizing the in-memory fuzzing capabilities of persistent mode, for another massive 41 | 2x speed boost over the previous harness. 42 | 43 | ```c 44 | #include "libxml/parser.h" 45 | #include "libxml/tree.h" 46 | #include 47 | 48 | __AFL_FUZZ_INIT(); 49 | 50 | int main(int argc, char **argv) { 51 | #ifdef __AFL_HAVE_MANUAL_CONTROL 52 | __AFL_INIT(); 53 | #endif 54 | unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT 55 | 56 | xmlInitParser(); 57 | while (__AFL_LOOP(1000)) { 58 | int len = __AFL_FUZZ_TESTCASE_LEN; 59 | xmlDocPtr doc = xmlReadMemory((char *)buf, len, "https://mykter.com", NULL, 0); 60 | if (doc != NULL) { 61 | xmlFreeDoc(doc); 62 | } 63 | } 64 | xmlCleanupParser(); 65 | 66 | return(0); 67 | } 68 | ``` 69 | 70 | With this wrapper and ASAN instrumentation, AFL should be able to find an out of bounds read in `xmlParseXMLDecl`. How 71 | long it takes to find is dependent on luck, but my single core persistent mode run took about 15 minutes @ ~4k exec/s. 72 | To speed things up beyond the persistent-mode gains, try multi-core fuzzing. There are some other bugs that it will also 73 | find, but they will likely take much longer. 74 | 75 | This challenge was adapted from the [Fuzzer Test Suite](https://github.com/google/fuzzer-test-suite/), which contains a 76 | wealth of real world fuzz targets and harnesses. 77 | 78 | AFLplusplus have also [written a tutorial](https://aflplus.plus/docs/tutorials/libxml2_tutorial/) around fuzzing this 79 | target, and include an example of how to fuzz it using QEMU mode, as if you didn't have the source code. 80 | -------------------------------------------------------------------------------- /challenges/libxml2/HINTS.md: -------------------------------------------------------------------------------- 1 | libxml2 has quite a broad API, but the most tempting target is the core XML parsing logic. This is going to be easy to 2 | fuzz, is definitely exposed to user-provided input in many situations, and is at high risk of containing bugs (parsing a 3 | complex data format in an unsafe language). 4 | 5 | This functionality is exposed in the [parser](http://xmlsoft.org/html/libxml-parser.html) API, and whilst you could dig 6 | through this documentation, the easiest approach is to look at an example. 7 | [`parse1.c`](http://xmlsoft.org/examples/parse1.c) (also in the repo under doc/examples/parse1.c) shows two core 8 | functions: `xmlReadFile` followed by `xmlFreeDoc`. 9 | 10 | Recall the type of program AFL can fuzz (from AFL's QuickStartGuide.txt): 11 | 12 | Find or write a reasonably fast and simple program that takes data from 13 | a file or stdin, processes it in a test-worthy way, then exits cleanly. 14 | 15 | We can wrap these two library functions in a little boiler-plate code to call xmlReadFile with a file specified as a 16 | commandline argument, and then free the resulting parsed document. As libxml can be compiled with clang and the API 17 | should be stateless, we can also include the AFL persistent-mode loop. 18 | 19 | Equivalently (looking at `parse3.c`), we could wrap `xmlReadMemory` with code that reads from stdin instead of a 20 | specified filename. 21 | 22 | Both of these approaches are good, but from here on we'll just look at the `xmlReadFile` option for simplicity. 23 | 24 | Once you've implemented the harness, compile it (refer back to README.md for the include & linker flags you need with 25 | libxml2), and then test your `fuzzer` executable by specifying an XML file on the commandline, e.g. 26 | `./fuzzer ./libxml2/regressions.xml`. There shouldn't be any visible result (unless you added some kind of output to 27 | your harness). We're now ready to fuzz in the usual manner for an ASAN-instrumented binary; here's a reminder of how to 28 | do it for the file-argument approach: 29 | 30 | ```shell 31 | mkdir in 32 | echo "" > in/hi.xml 33 | afl-fuzz -i in -o out ./harness @@ 34 | ``` 35 | 36 | This will work, but as XML isn't a compact binary format, a lot of its syntax will remain undiscovered by the fuzzer 37 | without further help. 38 | 39 | Note we're using the default memory limit, which (as of AFL++ 3.0c) is `-m none` - this is _probably_ safe in this 40 | instance, but there's always a chance the library will try and allocate a ridiculous amount of memory, which could cause 41 | system instability. We also are less likely to detect bugs where an attacker can cause the process to allocate 42 | disproportionately large amounts of memory. See AFL's `notes_for_asan.txt` doc for more robust approaches, one of which 43 | is also covered in the heartbleed challenge. 44 | -------------------------------------------------------------------------------- /challenges/libxml2/README.md: -------------------------------------------------------------------------------- 1 | libxml2 is a popular XML library. Libraries like this are perfect for fuzzing, they tick all the boxes: 2 | 3 | - Often parse user supplied data 4 | - Written in an unsafe language 5 | - Stateless 6 | - No network or filesystem interaction 7 | - The public documented API contains good targets - no need to identify and isolate an internal component to fuzz 8 | - Fast 9 | 10 | This makes it an ideal first target to write a fuzz harness for. 11 | 12 | We're going to try and find CVE-2015-8317. 13 | 14 | Build and test v2.9.2 with AFL and Address Sanitizer instrumentation by running: 15 | 16 | ```shell 17 | git submodule init && git submodule update 18 | cd libxml2 19 | CC=afl-clang-fast ./autogen.sh # you could also use afl-clang-lto, which is usally the better choice, but - oddly - in this case it takes longer to find the bug with an lto build. 20 | AFL_USE_ASAN=1 make -j 4 21 | # ./testModule # if you have compiled with ASAN, the tests fail - there are illegal memory accesses in the built-in test harness! 22 | # leak detection doesn't work in an unprivileged container as it can't attach to the process. 23 | # Run with ASAN_OPTIONS=detect_leaks=0 set to disable this ASAN feature, e.g. 24 | # ASAN_OPTIONS=detect_leaks=0 ./testModule 25 | ``` 26 | 27 | Now we have a working instrumented build of the library, but no fuzzing harness to use. 28 | 29 | Check out the docs - the [examples](http://xmlsoft.org/examples/index.html) are perhaps the easiest to grok - and 30 | consider what might be a good approach to creating a fuzzing harness. 31 | 32 | If you're comfortable experimenting or confident in your approach, implement a harness and see if you can find any bugs! 33 | Or, move right on to [HINTS.md](./HINTS.md) for some specific guidance on making a good libxml2 fuzzing harness. 34 | 35 | Once you've implemented a harness, you can compile it using a command like this: 36 | 37 | AFL_USE_ASAN=1 afl-clang-fast ./harness.c -I libxml2/include libxml2/.libs/libxml2.a -lz -lm -o fuzzer 38 | -------------------------------------------------------------------------------- /challenges/ntpq/ANSWERS-libFuzzer.md: -------------------------------------------------------------------------------- 1 | # Compiling 2 | 3 | The configure script will fail if you try and compile with `-fsanitize=fuzzer`, because it fails its sanity test of "can 4 | you compile a program". Instead, we configure with `-fsanitize-fuzzer-no-link`, but then manually override the CFLAGS 5 | used during actual compilation. 6 | 7 | 1. Configure the whole application with the `fuzzer-no-link` sanitizer: 8 | `CC=clang CFLAGS="-fsanitize=address,fuzzer-no-link -g" ./configure` 9 | 10 | 2. Build the ntpq binary using libFuzzer's main: `make clean && make -C ntpq CFLAGS="-fsanitize=address,fuzzer -g"` 11 | (note if you haven't replaced the existing main function yet, this will fail to link) 12 | 13 | # Running 14 | 15 | From the ntpq sub-directory: `./ntpq -dict /home/fuzzer/workshop/challenges/ntpq/ntpq.dict corpus` 16 | 17 | # Harness 18 | 19 | As per the AFL guidance, replace ntpq's existing main but with the usual LLVMFuzzerTestOneInput function. 20 | 21 | The tool will spit a lot of errors to stderr; you can replace all of this with /dev/null to make the fuzzer output more 22 | readable (and faster). 23 | 24 | For example: 25 | 26 | ```c 27 | FILE *fdevnull; 28 | 29 | int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) 30 | { 31 | if (fdevnull == NULL) 32 | { 33 | fdevnull = fopen("/dev/null", "w"); 34 | } 35 | if (size > 3) 36 | { 37 | int datatype = data[--size]; 38 | int status = data[--size]; 39 | cookedprint(datatype, size, data, status, 1, fdevnull); 40 | } 41 | return 0; 42 | } 43 | ``` 44 | 45 | And then search and replace `stderr` for `fdevnull` across the whole file. 46 | 47 | When you want to get coverage data, you'll need to provide your own `main` function that can take input from stdin or a 48 | file and call LLVMFuzzerTestOneInput on the contents. For example: 49 | 50 | ```c 51 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 52 | // no main 53 | #elseif FUZZING_COVERAGE_MODE 54 | int main(int argc, char **argv) 55 | { 56 | 57 | char data[4096] = {0}; 58 | int size = read(0, data, 4096); 59 | return LLVMFuzzerTestOneInput(data, size); 60 | } 61 | #else 62 | 63 | #endif 64 | ``` 65 | -------------------------------------------------------------------------------- /challenges/ntpq/ANSWERS.md: -------------------------------------------------------------------------------- 1 | # Fuzzing `cookedprint` 2 | 3 | Here's an example ntpqmain() replacement that uses deferred forkserver and persistent mode: 4 | 5 | #ifdef __AFL_HAVE_MANUAL_CONTROL 6 | __AFL_INIT(); 7 | #endif 8 | int datatype=0; 9 | int status=0; 10 | char data[1024*16] = {0}; 11 | int length=0; 12 | #ifdef __AFL_HAVE_MANUAL_CONTROL 13 | while (__AFL_LOOP(1000)) { 14 | #endif 15 | datatype=0; 16 | status=0; 17 | memset(data,0,1024*16); 18 | read(0, &datatype, 1); 19 | read(0, &status, 1); 20 | length = read(0, data, 1024 * 16); 21 | cookedprint(datatype, length, data, status, stdout); 22 | #ifdef __AFL_HAVE_MANUAL_CONTROL 23 | } 24 | #endif 25 | return 0; 26 | 27 | The 16kb buffer is fairly arbitrary - it could be that a smaller buffer would achieve comparable coverage at higher 28 | speed, or it could be that some bugs can't be hit because the limit is too low. 29 | 30 | If you just run with this, you'll notice that the stability percentage is a little lower than it could be. Read up on 31 | this in `docs/status_screen.txt`. It can be significantly improved by adding these lines to the start of `nextvar`, to 32 | ensure that these static variables don't retain data from one run to the next: 33 | 34 | memset(name, 0, sizeof(name)); 35 | memset(value, 0, sizeof(value)); 36 | 37 | # Coverage & Dictionaries 38 | 39 | Without any help, afl won't easily find all of the different formats that can be returned from `varfmt`. This is 40 | apparent when looking at the coverage output as described in HINTS.md - none of the cases except PADDING are hit. 41 | 42 | In `varfmt` we notice it is checking strings against entries in an array called `cookedvars`. These can be easily 43 | extracted into a dictionary for afl to use. Whilst we're at it, let's grab `tstflagnames` too as they look like they 44 | might be useful. 45 | 46 | In this case afl-clang-lto's auto-dictionary feature fails to detect the list of notable strings, perhaps because they 47 | are indirectly referenced. 48 | 49 | A dictionary with these keys in is included in this repo as `ntpq.dict`. Use it with the `-x` option to afl-fuzz, e.g.: 50 | 51 | afl-fuzz -i in -o out -x ntpq.dict ntp-4.2.8p8/ntpq/ntpq 52 | 53 | With the help of this dictionary you'll see the number of paths found shoot up. Some issues have been found in 4.2.8p10 54 | using this approach that have been reported but not fixed at the time of writing - if in doubt, contact 55 | security@ntp.org. 56 | -------------------------------------------------------------------------------- /challenges/ntpq/HINTS.md: -------------------------------------------------------------------------------- 1 | ## Test harness 2 | 3 | The original vulnerability is fairly accessible to afl. 4 | 5 | Rather than trying to have afl's output simulate a remote ntpd, just replace `ntpq/ntpq.c`'s main() function with code 6 | that calls cookedprint with `datatype`, `status`, and `data` all read in from stdin, and the output file as stdout. 7 | Whilst genuine responses from ntpd as seeds would help, they aren't neccessary. 8 | 9 | This is a common pattern in testing network programs - target functions such as parsers can often be easily tested in 10 | isolation. 11 | 12 | Compile with the classic `CC=afl-clang-fast ./configure && AFL_HARDEN=1 make -C ntpq` 13 | 14 | Note that this target also works with afl-clang-lto, provided you only try and compile ntpq not ntpd - that's the 15 | `-C ntpq` part of the last command. 16 | 17 | ## Coverage 18 | 19 | You may be able to find CVE-2009-0159 in a few minutes with no further work, especially if using persistent mode. You 20 | may not - it depends how lucky your run is. If you've found it, continue with these instructions using ntp-4.2.8p10; if 21 | you haven't, continue with these instructions on ntp-4.2.2. 22 | 23 | After running afl-fuzz for a while, you can check what coverage of `cookedprint` you've got. There are various options, 24 | here is how to use clang's gcov-compatible coverage tool: 25 | 26 | - Compile with `CC=clang CFLAGS="--coverage -g -O0" ./configure && make -C ntpq` (run `make distclean` first to be safe; 27 | ntpd has some problems compiling, so be sure to just compile ntpq) 28 | - Run the instrumented ntpq on all of the files in the queue (these correspond to all of the inputs that triggered a new 29 | path): 30 | 31 | `$ for F in out/queue/id* ; do ./ntp-4.2.8p10/ntpq/ntpq < $F > /dev/null ; done` 32 | 33 | - Compile all the coverage data into a gcov report: `cd ./ntp-4.2.8p10/ntpq/ && llvm-cov gcov ntpq.c` 34 | - Open the report (`./ntp-4.2.8p10/ntpq/ntpq.c.gcov`) in a text editor, and look at what parts of the file, and 35 | cookedprint in particular, aren't covered. From the gcov manpage: "The execution_count is '-' for lines containing no 36 | code. Unexecuted lines are marked #####" 37 | 38 | afl has a feature that will allow you to easily reach these unexplored areas - answers in ANSWERS.md! 39 | -------------------------------------------------------------------------------- /challenges/ntpq/README.md: -------------------------------------------------------------------------------- 1 | ntpq is a utility included as part of the NTP Reference Implementation suite of tools. It queries a server (e.g. ntpd) 2 | and provides information to the user. 3 | 4 | See if you can find CVE-2009-0159 in ntpq using afl. 5 | 6 | If you aren't using the Docker environment, v4.2.2 can be obtained from 7 | https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.2.tar.gz 8 | 9 | Otherwise, it is already present in the ntp-4.2.2 dir. 10 | 11 | You can see a writeup of the bug and the fix here: 12 | https://xorl.wordpress.com/2009/04/13/cve-2009-0159-ntp-remote-stack-overflow/ 13 | 14 | Use a coverage checker to see what parts of the target function you're exercising, then consider how to expand that 15 | coverage. 16 | 17 | Repeat the exercise on version 4.2.8p10, which has a fix. 18 | 19 | Compilation tip: compile ntpq by itself with `make -C ntpq`; trying to make everything includes ntpd, which is slower to 20 | compile and depending on how you're trying to compile it, might fail. 21 | -------------------------------------------------------------------------------- /challenges/ntpq/ntpq.dict: -------------------------------------------------------------------------------- 1 | "leap" 2 | "reach" 3 | "refid" 4 | "reftime" 5 | "clock" 6 | "org" 7 | "rec" 8 | "xmt" 9 | "flash" 10 | "srcadr" 11 | "peeradr" 12 | "dstadr" 13 | "filtdelay" 14 | "filtoffset" 15 | "filtdisp" 16 | "filterror" 17 | "pkt_dup" 18 | "pkt_bogus" 19 | "pkt_unsync" 20 | "pkt_denied" 21 | "pkt_auth" 22 | "pkt_stratum" 23 | "pkt_header" 24 | "pkt_autokey" 25 | "pkt_crypto" 26 | "peer_stratum" 27 | "peer_dist" 28 | "peer_loop" 29 | "peer_unreach" 30 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/ANSWERS-libFuzzer.md: -------------------------------------------------------------------------------- 1 | # Compiling 2 | 3 | CC=clang CFLAGS="-g -fsanitize=fuzzer -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -D_FORTIFY_SOURCE=2 4 | -fstack-protector-all" make 5 | 6 | Note as we aren't using ASAN, we turn on the less advanced mechanisms instead, akin to what AFL_HARDEN=1 does. Without 7 | these it can be really hard to diagnose what is going on. I don't know what exactly happens when you try and use ASAN, 8 | but it seems unable to progress at all. 9 | 10 | # Running 11 | 12 | ./m1-bad 13 | 14 | (yuck: this program dumps onto stdout) 15 | 16 | # Harness 17 | 18 | This challenge has a requirement that is likely to crop up somewhat regularly: the target works on a file pointer, not 19 | on a buffer. What do? `` has an answer: `fmemopen`, which presents a file interface to an in-memory buffer. 20 | 21 | Definitely worth finding and deleting all of the printf statements in mime1-bad.c. 22 | 23 | Copy the original main function to LLVMFuzzerTestOneInput, and hide it behind an #ifdef guard. Firstly delete the 24 | commandline processing part: 25 | 26 | ```c 27 | assert(argc == 2); 28 | temp = fopen(argv[1], "r"); 29 | assert(temp != NULL); 30 | ``` 31 | 32 | And then after the code that sets up the static `header` struct and initializes the envelope, arrange to pass in the 33 | test case as a file, and close it afterwards: 34 | 35 | ```c 36 | if (len == 0) 37 | { 38 | // otherwise fmemopen will fail 39 | return 0; 40 | } 41 | e->e_dfp = fmemopen(data, len, "r"); 42 | assert(e->e_dfp != NULL); 43 | 44 | mime7to8(header, e); 45 | 46 | fclose(e->e_dfp); 47 | ``` 48 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/ANSWERS.md: -------------------------------------------------------------------------------- 1 | If you've got AFL working then even without a good seed or parallelisation it might find a crashing input in the order 2 | of 15 minutes (subject to how fast your computer is and how lucky you get). It might take much longer. 3 | 4 | With the cheater-seed from HINTS you should find a crash in just a few minutes. 5 | 6 | With parallelisation and deferred forkserver / persistent mode you will get the corresponding performance increases 7 | which should find a crash in a few minutes even without the seed. 8 | 9 | Here's a sample crashing input after being put through the afl-tmin grinder: 10 | 11 | 0000000000000000000= 12 | 000000000000000000000000000000000000000000= 13 | 14 | (now you can see why the sample seed was somewhat unfair) 15 | 16 | Try taking a look at the source code or running the program under gdb to see why this leads to a buffer overflow. 17 | 18 | If you put some of your crashing inputs through afl-tmin, do you get the same input as above each time? If you don't, it 19 | means that afl-tmin couldn't shrink the inputs any further without leading to a crash, yet they are still different - to 20 | understand why you'll have to turn to the source. 21 | 22 | Vulnerability source: CVE-1999-0206, https://samate.nist.gov/SRD/view_testcase.php?tID=1301 23 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/HINTS.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | This sample comes with a handy ready-made wrapper. main.c takes a file specified on the commandline rather than stdin, 4 | but that will suit our purposes. 5 | 6 | Compile with something like: 7 | 8 | make clean 9 | CC=afl-clang-fast make 10 | 11 | (you could consider adding deferred forkserver or persistent mode support, or compiling with AFL_HARDEN=1 or ASAN) 12 | 13 | # Running 14 | 15 | Give afl a seed to run on: 16 | 17 | mkdir in 18 | echo a > in/1 19 | 20 | Then you can tell afl-fuzz how to pass the filename to the target program using an "@@" notation: 21 | 22 | afl-fuzz -i in -o out ./m1-bad @@ 23 | 24 | # Seeds 25 | 26 | Whilst afl does have a very impressive emergent synthesis capability, choosing good input files is an important 27 | prerequisite for many targets. 28 | 29 | If you want to speed up your fuzzing performance on this target, you can do better than seeding it with "a". It's a mime 30 | parser, so if you know the format you could hand-write a few examples. Or you could fetch some off the web or another 31 | source (e.g. your own email archives if in a suitable format). 32 | 33 | Here's a helper seed to speed things up: 34 | 35 | echo -e "a=\nb=" > in/multiline 36 | 37 | Note that files in "in" aren't monitored - you have to put them there before starting the fuzzer. 38 | 39 | (This is definitely cheating by the way, a real sample set would include several other inputs, 'diluting' the search 40 | space to some extent. I provide it (a) for if you get bored waiting for your fuzzer and don't want to go parallel and 41 | (b) to demonstrate the performance difference - watch how long it takes to find paths with this input vs the "a" input.) 42 | 43 | # Persistent mode and reproducibility 44 | 45 | I have experienced non-reproducible crashes when using persistent mode with this target. It looks like it finds the bug, 46 | but the bug doesn't cause a crash straight-away - it corrupts memory somewhere, and as we aren't using ASAN it isn't 47 | immediately detected. A later run then leads to a crash. If you experience this too, simply remove the persistent mode 48 | loop and resume your fuzzing run (`afl-fuzz -i- ...`) - it should quickly find a reproducible version of the crash now 49 | it has a great starting point. 50 | 51 | Take-away: persistent mode without ASAN might make crash triage harder. 52 | 53 | # Answers 54 | 55 | See ANSWERS 56 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -g 2 | 3 | m1-bad: mime1-bad.c main.c 4 | ${CC} ${CFLAGS} -o m1-bad mime1-bad.c main.c -I . 5 | 6 | clean: 7 | rm -f *-bad *-ok -------------------------------------------------------------------------------- /challenges/sendmail/1301/README.md: -------------------------------------------------------------------------------- 1 | An extract from sendmail, the ubiquitous SMTP server. 2 | 3 | This function processes email bodies to convert between 7-bit MIME and 8-bit MIME. 4 | 5 | The program already comes with a test harness around it (in `main.c`), that runs the conversion routine on a file 6 | specified on the commandline. 7 | 8 | To test it out: 9 | 10 | make 11 | echo "hi!" > input 12 | ./m1-bad input 13 | 14 | You can see from the output (or the source) that the code has been fairly extensively instrumented with debug prints to 15 | watch out for a buffer overflow. Fortunately we don't need to worry about this: afl is going to find the problems for 16 | us. 17 | 18 | I recommend you experiment with persistent mode and/or a multicore setup for this challenge. 19 | 20 | For multicore read afl's docs/parallel_fuzzing.txt for fairly straightforward instructions on single-system fuzzing. Get 21 | a main and 3 secondaries running to slash the time it takes to solve this challenge. 22 | 23 | For persistent mode read `AFLplusplus/instrumentation/README.persistent_mode.md`. There are some gotchas with this 24 | target! See HINTS.md 25 | 26 | For hints on fuzzing, see HINTS. If you get your fuzzer running without needing the hints, read the HINTS file whilst 27 | watching the UI for some dicussion on seed file selection. Also check out docs/status_screen.txt. 28 | 29 | Warning: this challenge doesn't play nicely with ASAN. Save ASAN for another challenge like heartbleed. 30 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/main.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | MIT Copyright Notice 5 | 6 | Copyright 2003 M.I.T. 7 | 8 | Permission is hereby granted, without written agreement or royalty fee, to use, 9 | copy, modify, and distribute this software and its documentation for any 10 | purpose, provided that the above copyright notice and the following three 11 | paragraphs appear in all copies of this software. 12 | 13 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 14 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 15 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 16 | SUCH DAMANGE. 17 | 18 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 20 | AND NON-INFRINGEMENT. 21 | 22 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | 25 | $Author: tleek $ 26 | $Date: 2004/01/05 17:27:43 $ 27 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/main.c,v 1.1.1.1 2004/01/05 17:27:43 tleek Exp $ 28 | 29 | 30 | 31 | */ 32 | 33 | /* 34 | 35 | Sendmail Copyright Notice 36 | 37 | 38 | Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 39 | All rights reserved. 40 | Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 41 | Copyright (c) 1988, 1993 42 | The Regents of the University of California. All rights reserved. 43 | 44 | By using this file, you agree to the terms and conditions set 45 | forth in the LICENSE file which can be found at the top level of 46 | the sendmail distribution. 47 | 48 | 49 | $Author: tleek $ 50 | $Date: 2004/01/05 17:27:43 $ 51 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/main.c,v 1.1.1.1 2004/01/05 17:27:43 tleek Exp $ 52 | 53 | 54 | 55 | */ 56 | 57 | /* 58 | 59 | 60 | 61 | */ 62 | 63 | #include "my-sendmail.h" 64 | #include 65 | 66 | int main(int argc, char **argv) 67 | { 68 | 69 | HDR *header; 70 | register ENVELOPE *e; 71 | FILE *temp; 72 | 73 | assert(argc == 2); 74 | temp = fopen(argv[1], "r"); 75 | assert(temp != NULL); 76 | 77 | header = (HDR *)malloc(sizeof(struct header)); 78 | 79 | header->h_field = "Content-Transfer-Encoding"; 80 | header->h_value = "quoted-printable"; 81 | header->h_link = NULL; 82 | header->h_flags = 0; 83 | 84 | e = (ENVELOPE *)malloc(sizeof(struct envelope)); 85 | e->e_id = "First Entry"; 86 | e->e_dfp = temp; 87 | 88 | mime7to8(header, e); 89 | 90 | fclose(temp); 91 | free(e); 92 | free(header); 93 | 94 | return 0; 95 | } 96 | 97 | /* 98 | 99 | 100 | 101 | */ 102 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/mime1-bad.c: -------------------------------------------------------------------------------- 1 | // I've just removed some debug prints from the original file. -- Michael Macnair 2 | 3 | /* 4 | 5 | MIT Copyright Notice 6 | 7 | Copyright 2003 M.I.T. 8 | 9 | Permission is hereby granted, without written agreement or royalty fee, to use, 10 | copy, modify, and distribute this software and its documentation for any 11 | purpose, provided that the above copyright notice and the following three 12 | paragraphs appear in all copies of this software. 13 | 14 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 15 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 16 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 17 | SUCH DAMANGE. 18 | 19 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 20 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 21 | AND NON-INFRINGEMENT. 22 | 23 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 24 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 25 | 26 | $Author: tleek $ 27 | $Date: 2004/02/05 15:19:59 $ 28 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/mime1-bad.c,v 1.2 2004/02/05 15:19:59 tleek Exp $ 29 | 30 | 31 | 32 | */ 33 | 34 | 35 | /* 36 | 37 | Sendmail Copyright Notice 38 | 39 | 40 | Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 41 | All rights reserved. 42 | Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 43 | Copyright (c) 1988, 1993 44 | The Regents of the University of California. All rights reserved. 45 | 46 | By using this file, you agree to the terms and conditions set 47 | forth in the LICENSE file which can be found at the top level of 48 | the sendmail distribution. 49 | 50 | 51 | $Author: tleek $ 52 | $Date: 2004/02/05 15:19:59 $ 53 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/mime1-bad.c,v 1.2 2004/02/05 15:19:59 tleek Exp $ 54 | 55 | 56 | 57 | */ 58 | 59 | 60 | /* 61 | 62 | 63 | 64 | */ 65 | 66 | # include 67 | # include "my-sendmail.h" 68 | 69 | 70 | char * 71 | xalloc(sz) 72 | register int sz; 73 | { 74 | register char *p; 75 | 76 | /* some systems can't handle size zero mallocs */ 77 | if (sz <= 0) 78 | sz = 1; 79 | 80 | p = malloc((unsigned) sz); 81 | if (p == NULL) 82 | { 83 | perror("Out of memory!!"); 84 | } 85 | return (p); 86 | } 87 | 88 | char * hvalue(char *, HDR *); 89 | 90 | /* 91 | ** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format 92 | ** 93 | ** This is a hack. Supports translating the two 7-bit body-encodings 94 | ** (quoted-printable and base64) to 8-bit coded bodies. 95 | ** 96 | ** There is not much point in supporting multipart here, as the UA 97 | ** will be able to deal with encoded MIME bodies if it can parse MIME 98 | ** multipart messages. 99 | ** 100 | ** Note also that we wont be called unless it is a text/plain MIME 101 | ** message, encoded base64 or QP and mailer flag '9' has been defined 102 | ** on mailer. 103 | ** 104 | ** Parameters: 105 | ** header -- the header for this body part. 106 | ** e -- envelope. 107 | ** 108 | ** Returns: 109 | ** none. 110 | */ 111 | 112 | 113 | void 114 | mime7to8(header, e) 115 | HDR *header; 116 | register ENVELOPE *e; 117 | { 118 | register char *p; 119 | u_char *obp; 120 | char buf[MAXLINE]; 121 | char canary[10]; 122 | u_char obuf[MAXLINE]; 123 | 124 | strcpy(canary, "GOOD"); /* use canary to see if obuf gets overflowed */ 125 | 126 | p = (char *) hvalue("Content-Transfer-Encoding", header); 127 | if (p == NULL) 128 | { 129 | printf("Content-Transfer-Encoding not found in header\n"); 130 | return; 131 | } 132 | 133 | /* 134 | ** Translate body encoding to 8-bit. Supports two types of 135 | ** encodings; "base64" and "quoted-printable". Assume qp if 136 | ** it is not base64. 137 | */ 138 | 139 | /* Misha: This is greatly modified */ 140 | 141 | if (strcasecmp(p, "base64") == 0) 142 | { 143 | printf("We do not handle base64 encoding...\n"); 144 | return; 145 | } 146 | else 147 | { 148 | /* quoted-printable */ 149 | obp = obuf; 150 | while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 151 | { 152 | printf ("buf-obuf=%u\n", buf-(char *)obuf); 153 | printf ("obp-obuf=%u\n", obp-obuf); 154 | printf ("canary-obuf=%u\n", canary-(char *)obuf); 155 | 156 | if (mime_fromqp((u_char *) buf, &obp, 0, MAXLINE) == 0) { 157 | printf("canary = %s\n", canary); 158 | continue; 159 | } 160 | /* 161 | putline((char *) obuf, mci); 162 | */ 163 | obp = obuf; 164 | printf("canary = %s\n", canary); 165 | } 166 | 167 | } 168 | 169 | printf("obuf = %s\n",obuf); 170 | printf("canary should be GOOD\n"); 171 | printf("canary = %s\n", canary); 172 | } 173 | 174 | 175 | /* 176 | ** The following is based on Borenstein's "codes.c" module, with 177 | ** simplifying changes as we do not deal with multipart, and to do 178 | ** the translation in-core, with an attempt to prevent overrun of 179 | ** output buffers. 180 | ** 181 | ** What is needed here are changes to defined this code better against 182 | ** bad encodings. Questionable to always return 0xFF for bad mappings. 183 | */ 184 | 185 | static char index_hex[128] = 186 | { 187 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 188 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 189 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 190 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, 191 | -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 192 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 193 | -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 194 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 195 | }; 196 | 197 | #define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) 198 | 199 | int 200 | mime_fromqp(infile, outfile, state, maxlen) 201 | u_char *infile; 202 | u_char **outfile; 203 | int state; /* Decoding body (0) or header (1) */ 204 | int maxlen; /* Max # of chars allowed in outfile */ 205 | { 206 | u_char *ooutfile; 207 | int c1, c2; 208 | int nchar = 0; 209 | 210 | ooutfile = *outfile; 211 | 212 | while ((c1 = *infile++) != '\0') 213 | { 214 | if (c1 == '=') 215 | { 216 | if ((c1 = *infile++) == 0) 217 | break; 218 | 219 | if (c1 == '\n') /* ignore it */ 220 | { 221 | if (state == 0) 222 | return 0; 223 | } 224 | else 225 | { 226 | if ((c2 = *infile++) == '\0') 227 | break; 228 | 229 | c1 = HEXCHAR(c1); 230 | c2 = HEXCHAR(c2); 231 | 232 | if (++nchar > maxlen) 233 | break; 234 | 235 | /* BAD */ 236 | *(*outfile)++ = c1 << 4 | c2; 237 | } 238 | } 239 | else 240 | { 241 | if (state == 1 && c1 == '_') 242 | c1 = ' '; 243 | 244 | if (++nchar > maxlen) 245 | break; 246 | 247 | /*BAD*/ 248 | *(*outfile)++ = c1; 249 | 250 | if (c1 == '\n') 251 | break; 252 | } 253 | } 254 | 255 | 256 | /*BAD*/ 257 | *(*outfile)++ = '\0'; 258 | return 1; 259 | } 260 | 261 | 262 | /* 263 | ** HVALUE -- return value of a header. 264 | ** 265 | ** Only "real" fields (i.e., ones that have not been supplied 266 | ** as a default) are used. 267 | ** 268 | ** Parameters: 269 | ** field -- the field name. 270 | ** header -- the header list. 271 | ** 272 | ** Returns: 273 | ** pointer to the value part. 274 | ** NULL if not found. 275 | ** 276 | ** Side Effects: 277 | ** none. 278 | */ 279 | 280 | char * hvalue(field, header) 281 | char *field; 282 | HDR *header; 283 | { 284 | register HDR *h; 285 | 286 | for (h = header; h != NULL; h = h->h_link) 287 | { 288 | if (!bitset(H_DEFAULT, h->h_flags) && 289 | strcasecmp(h->h_field, field) == 0) 290 | return (h->h_value); 291 | } 292 | return (NULL); 293 | } 294 | 295 | 296 | 297 | /* 298 | 299 | 300 | 301 | */ 302 | 303 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/mime1-bad.c.orig: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | MIT Copyright Notice 5 | 6 | Copyright 2003 M.I.T. 7 | 8 | Permission is hereby granted, without written agreement or royalty fee, to use, 9 | copy, modify, and distribute this software and its documentation for any 10 | purpose, provided that the above copyright notice and the following three 11 | paragraphs appear in all copies of this software. 12 | 13 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 14 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 15 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 16 | SUCH DAMANGE. 17 | 18 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 20 | AND NON-INFRINGEMENT. 21 | 22 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | 25 | $Author: tleek $ 26 | $Date: 2004/02/05 15:19:59 $ 27 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/mime1-bad.c,v 1.2 2004/02/05 15:19:59 tleek Exp $ 28 | 29 | 30 | 31 | */ 32 | 33 | 34 | /* 35 | 36 | Sendmail Copyright Notice 37 | 38 | 39 | Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 40 | All rights reserved. 41 | Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 42 | Copyright (c) 1988, 1993 43 | The Regents of the University of California. All rights reserved. 44 | 45 | By using this file, you agree to the terms and conditions set 46 | forth in the LICENSE file which can be found at the top level of 47 | the sendmail distribution. 48 | 49 | 50 | $Author: tleek $ 51 | $Date: 2004/02/05 15:19:59 $ 52 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/mime1-bad.c,v 1.2 2004/02/05 15:19:59 tleek Exp $ 53 | 54 | 55 | 56 | */ 57 | 58 | 59 | /* 60 | 61 | 62 | 63 | */ 64 | 65 | # include 66 | #include 67 | # include "my-sendmail.h" 68 | 69 | 70 | char * 71 | xalloc(sz) 72 | register int sz; 73 | { 74 | register char *p; 75 | 76 | /* some systems can't handle size zero mallocs */ 77 | if (sz <= 0) 78 | sz = 1; 79 | 80 | p = malloc((unsigned) sz); 81 | if (p == NULL) 82 | { 83 | perror("Out of memory!!"); 84 | } 85 | return (p); 86 | } 87 | 88 | char * hvalue(char *, HDR *); 89 | 90 | /* 91 | ** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format 92 | ** 93 | ** This is a hack. Supports translating the two 7-bit body-encodings 94 | ** (quoted-printable and base64) to 8-bit coded bodies. 95 | ** 96 | ** There is not much point in supporting multipart here, as the UA 97 | ** will be able to deal with encoded MIME bodies if it can parse MIME 98 | ** multipart messages. 99 | ** 100 | ** Note also that we wont be called unless it is a text/plain MIME 101 | ** message, encoded base64 or QP and mailer flag '9' has been defined 102 | ** on mailer. 103 | ** 104 | ** Parameters: 105 | ** header -- the header for this body part. 106 | ** e -- envelope. 107 | ** 108 | ** Returns: 109 | ** none. 110 | */ 111 | 112 | 113 | void 114 | mime7to8(header, e) 115 | HDR *header; 116 | register ENVELOPE *e; 117 | { 118 | register char *p; 119 | u_char *obp; 120 | char buf[MAXLINE]; 121 | char canary[10]; 122 | u_char obuf[MAXLINE]; 123 | 124 | strcpy(canary, "GOOD"); /* use canary to see if obuf gets overflowed */ 125 | 126 | p = (char *) hvalue("Content-Transfer-Encoding", header); 127 | if (p == NULL) 128 | { 129 | printf("Content-Transfer-Encoding not found in header\n"); 130 | return; 131 | } 132 | 133 | /* 134 | ** Translate body encoding to 8-bit. Supports two types of 135 | ** encodings; "base64" and "quoted-printable". Assume qp if 136 | ** it is not base64. 137 | */ 138 | 139 | /* Misha: This is greatly modified */ 140 | 141 | if (strcasecmp(p, "base64") == 0) 142 | { 143 | printf("We do not handle base64 encoding...\n"); 144 | return; 145 | } 146 | else 147 | { 148 | /* quoted-printable */ 149 | obp = obuf; 150 | while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 151 | { 152 | printf ("buf-obuf=%u\n", buf-(char *)obuf); 153 | printf ("obp-obuf=%u\n", obp-obuf); 154 | printf ("canary-obuf=%u\n", canary-(char *)obuf); 155 | 156 | if (mime_fromqp((u_char *) buf, &obp, 0, MAXLINE) == 0) { 157 | printf("canary = %s\n", canary); 158 | continue; 159 | } 160 | /* 161 | putline((char *) obuf, mci); 162 | */ 163 | obp = obuf; 164 | printf("canary = %s\n", canary); 165 | } 166 | 167 | } 168 | 169 | printf("obuf = %s\n",obuf); 170 | printf("canary should be GOOD\n"); 171 | printf("canary = %s\n", canary); 172 | } 173 | 174 | 175 | /* 176 | ** The following is based on Borenstein's "codes.c" module, with 177 | ** simplifying changes as we do not deal with multipart, and to do 178 | ** the translation in-core, with an attempt to prevent overrun of 179 | ** output buffers. 180 | ** 181 | ** What is needed here are changes to defined this code better against 182 | ** bad encodings. Questionable to always return 0xFF for bad mappings. 183 | */ 184 | 185 | static char index_hex[128] = 186 | { 187 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 188 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 189 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 190 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, 191 | -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 192 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 193 | -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 194 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 195 | }; 196 | 197 | #define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) 198 | 199 | int 200 | mime_fromqp(infile, outfile, state, maxlen) 201 | u_char *infile; 202 | u_char **outfile; 203 | int state; /* Decoding body (0) or header (1) */ 204 | int maxlen; /* Max # of chars allowed in outfile */ 205 | { 206 | u_char *ooutfile; 207 | int c1, c2; 208 | int nchar = 0; 209 | 210 | ooutfile = *outfile; 211 | 212 | while ((c1 = *infile++) != '\0') 213 | { 214 | if (c1 == '=') 215 | { 216 | if ((c1 = *infile++) == 0) 217 | break; 218 | 219 | if (c1 == '\n') /* ignore it */ 220 | { 221 | if (state == 0) 222 | return 0; 223 | } 224 | else 225 | { 226 | if ((c2 = *infile++) == '\0') 227 | break; 228 | 229 | c1 = HEXCHAR(c1); 230 | c2 = HEXCHAR(c2); 231 | 232 | if (++nchar > maxlen) 233 | break; 234 | 235 | printf ("1: outfile - ooutfile = %d maxlen) 248 | break; 249 | 250 | printf ("2: outfile - ooutfile = %d h_link) 296 | { 297 | if (!bitset(H_DEFAULT, h->h_flags) && 298 | strcasecmp(h->h_field, field) == 0) 299 | return (h->h_value); 300 | } 301 | return (NULL); 302 | } 303 | 304 | 305 | 306 | /* 307 | 308 | 309 | 310 | */ 311 | 312 | -------------------------------------------------------------------------------- /challenges/sendmail/1301/my-sendmail.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | MIT Copyright Notice 5 | 6 | Copyright 2003 M.I.T. 7 | 8 | Permission is hereby granted, without written agreement or royalty fee, to use, 9 | copy, modify, and distribute this software and its documentation for any 10 | purpose, provided that the above copyright notice and the following three 11 | paragraphs appear in all copies of this software. 12 | 13 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 14 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 15 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 16 | SUCH DAMANGE. 17 | 18 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 20 | AND NON-INFRINGEMENT. 21 | 22 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | 25 | $Author: tleek $ 26 | $Date: 2004/01/05 17:27:43 $ 27 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/my-sendmail.h,v 1.1.1.1 2004/01/05 17:27:43 tleek Exp $ 28 | 29 | 30 | 31 | */ 32 | 33 | 34 | /* 35 | 36 | Sendmail Copyright Notice 37 | 38 | 39 | Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 40 | All rights reserved. 41 | Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 42 | Copyright (c) 1988, 1993 43 | The Regents of the University of California. All rights reserved. 44 | 45 | By using this file, you agree to the terms and conditions set 46 | forth in the LICENSE file which can be found at the top level of 47 | the sendmail distribution. 48 | 49 | 50 | $Author: tleek $ 51 | $Date: 2004/01/05 17:27:43 $ 52 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s3/my-sendmail.h,v 1.1.1.1 2004/01/05 17:27:43 tleek Exp $ 53 | 54 | 55 | 56 | */ 57 | 58 | 59 | /* 60 | 61 | 62 | 63 | */ 64 | 65 | #include 66 | #include 67 | /* 68 | #include 69 | */ 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | extern int mime_fromqp(u_char *, u_char ** ,int,int); 76 | 77 | /* I have cut out the BITMAP field of header */ 78 | struct header 79 | { 80 | char *h_field; /* the name of the field */ 81 | char *h_value; /* the value of that field */ 82 | struct header *h_link; /* the next header */ 83 | u_short h_flags; /* status bits, see below */ 84 | 85 | }; 86 | 87 | typedef struct header HDR; 88 | 89 | /* modified address structure */ 90 | struct address 91 | { 92 | char *q_paddr; /* the printname for the address */ 93 | char *q_user; /* user name */ 94 | char *q_ruser; /* real user name, or NULL if q_user */ 95 | char *q_host; /* host name */ 96 | /*struct mailer *q_mailer;*/ /* mailer to use */ 97 | u_long q_flags; /* status flags, see below */ 98 | uid_t q_uid; /* user-id of receiver (if known) */ 99 | gid_t q_gid; /* group-id of receiver (if known) */ 100 | char *q_home; /* home dir (local mailer only) */ 101 | char *q_fullname; /* full name if known */ 102 | struct address *q_next; /* chain */ 103 | struct address *q_alias; /* address this results from */ 104 | char *q_owner; /* owner of q_alias */ 105 | struct address *q_tchain; /* temporary use chain */ 106 | char *q_orcpt; /* ORCPT parameter from RCPT TO: line */ 107 | char *q_status; /* status code for DSNs */ 108 | char *q_rstatus; /* remote status message for DSNs */ 109 | /*time_t q_statdate; */ /* date of status messages */ 110 | char *q_statmta; /* MTA generating q_rstatus */ 111 | short q_specificity; /* how "specific" this address is */ 112 | }; 113 | 114 | typedef struct address ADDRESS; 115 | 116 | 117 | /* modified envelope structure */ 118 | struct envelope 119 | { 120 | HDR *e_header; /* head of header list */ 121 | long e_msgpriority; /* adjusted priority of this message */ 122 | time_t e_ctime; /* time message appeared in the queue */ 123 | char *e_to; /* the target person */ 124 | ADDRESS e_from; /* the person it is from */ 125 | char *e_sender; /* e_from.q_paddr w comments stripped */ 126 | char **e_fromdomain; /* the domain part of the sender */ 127 | ADDRESS *e_sendqueue; /* list of message recipients */ 128 | ADDRESS *e_errorqueue; /* the queue for error responses */ 129 | long e_msgsize; /* size of the message in bytes */ 130 | long e_flags; /* flags, see below */ 131 | int e_nrcpts; /* number of recipients */ 132 | short e_class; /* msg class (priority, junk, etc.) */ 133 | short e_hopcount; /* number of times processed */ 134 | short e_nsent; /* number of sends since checkpoint */ 135 | short e_sendmode; /* message send mode */ 136 | short e_errormode; /* error return mode */ 137 | short e_timeoutclass; /* message timeout class */ 138 | struct envelope *e_parent; /* the message this one encloses */ 139 | struct envelope *e_sibling; /* the next envelope of interest */ 140 | char *e_bodytype; /* type of message body */ 141 | FILE *e_dfp; /* temporary file */ 142 | char *e_id; /* code for this entry in queue */ 143 | FILE *e_xfp; /* transcript file */ 144 | FILE *e_lockfp; /* the lock file for this message */ 145 | char *e_message; /* error message */ 146 | char *e_statmsg; /* stat msg (changes per delivery) */ 147 | char *e_msgboundary; /* MIME-style message part boundary */ 148 | char *e_origrcpt; /* original recipient (one only) */ 149 | char *e_envid; /* envelope id from MAIL FROM: line */ 150 | char *e_status; /* DSN status for this message */ 151 | time_t e_dtime; /* time of last delivery attempt */ 152 | int e_ntries; /* number of delivery attempts */ 153 | dev_t e_dfdev; /* df file's device, for crash recov */ 154 | ino_t e_dfino; /* df file's ino, for crash recovery */ 155 | char *e_macro[256]; /* macro definitions */ 156 | }; 157 | 158 | 159 | typedef struct envelope ENVELOPE; 160 | 161 | 162 | # define bitset(bit, word) (((word) & (bit)) != 0) 163 | # define H_DEFAULT 0x0004 /* if another value is found, drop this */ 164 | # define MAXLINE 50 /* modified max line length */ 165 | 166 | extern char *xalloc(int); 167 | extern void mime7to8(HDR *, ENVELOPE *); 168 | 169 | /* make a copy of a string */ 170 | #define newstr(s) strcpy(xalloc(strlen(s) + 1), s) 171 | 172 | 173 | /* 174 | 175 | 176 | 177 | */ 178 | 179 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/ANSWERS.md: -------------------------------------------------------------------------------- 1 | This vulnerability is not a quick catch with the afl approach. 2 | 3 | Example crashing input: 4 | 5 | python -c 'print 60*("\\"+"\xff")' | ./prescan-bad 6 | 7 | In a short test run my instance produced an input file with a large number of backlashes. This is on the right path: it 8 | will have been found because the hitcount for a particular pair of basic blocks is higher with each backslash. It also 9 | found the 0xFF character as being interesting - various test cases have runs of it. 10 | 11 | However to actually crash the program, you need a long combo of "\<0xff>" pairs, or similar (I haven't studied the 12 | vulnerability). afl will eventually find this through splicing and mutating the test cases mentioned above - whether it 13 | finds it quickly depends on whether the instrumentation guides it towards it. 14 | 15 | It will hit it at some point, but it's not clear how long it will take. This is a good example of how it can be hard to 16 | determine when to stop fuzzing. It will go through a lot of cycles without finding any new paths, but each of those 17 | cycles are very quick. A code coverage tool will likely say we've hit every line of code. 18 | 19 | Deferred initialization makes a significant different - just under 1.5x performance improvement for me. The obvious 20 | place to put it is immediately before the call to read(). 21 | 22 | Persistent mode makes a massive difference. This is a very fast target (it's just scanning a 50char string), so the 23 | overhead of forking represents a very large proportion of the total time. I get a ~4x performance improvement on top of 24 | deferred mode. BUT - is it safe? Is it actually representative testing after the first loop? There are a few global 25 | variables - you would have to check the code to make sure these weren't maintaining state that affected the process 26 | between calls to `parseaddr()`. If you were fuzzing this for real and found that this wasn't safe, then given the 27 | performance improvement it might be worth it modifying the code to make it stateless. 28 | 29 | This is the output from running afl-tmin on the example crashing input from above: 30 | 31 | 000000000000000000000000000000000000000000000\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\� 32 | 33 | As you can see, this is markedly simpler than the original demo - debugging a crash with this input would likely be a 34 | lot easier. 35 | 36 | Vulnerability source: CVE-2003-0161, https://samate.nist.gov/SRD/view_testcase.php?tID=1305 37 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/HINTS.md: -------------------------------------------------------------------------------- 1 | You'll want to adapt this harness to read the input from stdin instead of using a fixed address. 2 | 3 | To skip this step, just do: 4 | 5 | cp prescan-overflow-bad-fuzz.c prescan-overflow-bad.c 6 | 7 | The makefile uses CC as per normal, so the standard compilation approach will work, e.g.: 8 | 9 | CC=afl-clang-fast AFL_USE_ASAN=1 make 10 | 11 | A sensible seed might be your email address, e.g. 12 | 13 | echo -n "michael@mykter.com" > in/seed 14 | 15 | You may want to experiment with the deferred fork server and persistent mode, to see how much more performance you can 16 | eke out of it. 17 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -g 2 | 3 | all: prescan-bad 4 | 5 | clean: 6 | rm -f *-bad *-ok 7 | 8 | prescan-bad: prescan-overflow-bad.c 9 | $(CC) $(CFLAGS) -g -o prescan-bad prescan-overflow-bad.c 10 | 11 | 12 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/README.md: -------------------------------------------------------------------------------- 1 | An extract from sendmail, the ubiquitous SMTP server. 2 | 3 | This function preprocesses email addresses to normalize them, for later use by the program. 4 | 5 | The program already comes with a test harness around it (the main() function), that runs the prescan routine on a fixed 6 | email address. 7 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/prescan-overflow-bad-fuzz.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | MIT Copyright Notice 5 | 6 | Copyright 2003 M.I.T. 7 | 8 | Permission is hereby granted, without written agreement or royalty fee, to use, 9 | copy, modify, and distribute this software and its documentation for any 10 | purpose, provided that the above copyright notice and the following three 11 | paragraphs appear in all copies of this software. 12 | 13 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 14 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 15 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 16 | SUCH DAMANGE. 17 | 18 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 20 | AND NON-INFRINGEMENT. 21 | 22 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | 25 | $Author: tleek $ 26 | $Date: 2004/01/05 17:27:45 $ 27 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s5/prescan-overflow-bad.c,v 1.1.1.1 2004/01/05 17:27:45 tleek Exp $ 28 | 29 | 30 | 31 | */ 32 | 33 | 34 | /* 35 | 36 | Sendmail Copyright Notice 37 | 38 | 39 | Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 40 | All rights reserved. 41 | Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 42 | Copyright (c) 1988, 1993 43 | The Regents of the University of California. All rights reserved. 44 | 45 | By using this file, you agree to the terms and conditions set 46 | forth in the LICENSE file which can be found at the top level of 47 | the sendmail distribution. 48 | 49 | 50 | $Author: tleek $ 51 | $Date: 2004/01/05 17:27:45 $ 52 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s5/prescan-overflow-bad.c,v 1.1.1.1 2004/01/05 17:27:45 tleek Exp $ 53 | 54 | 55 | 56 | */ 57 | 58 | 59 | /* 60 | 61 | 62 | 63 | */ 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | /* states and character types */ 73 | #define OPR 0 /* operator */ 74 | #define ATM 1 /* atom */ 75 | #define QST 2 /* in quoted string */ 76 | #define SPC 3 /* chewing up spaces */ 77 | #define ONE 4 /* pick up one character */ 78 | #define ILL 5 /* illegal character */ 79 | 80 | #define NSTATES 6 /* number of states */ 81 | 82 | #define MAXNAME 40 /* max length of a name */ 83 | #define MAXATOM 10 /* max atoms per address */ 84 | #define PSBUFSIZE (MAXNAME + MAXATOM) 85 | 86 | #define MAXMACROID 0377 /* max macro id number */ 87 | 88 | #define TYPE 017 /* mask to select state type */ 89 | 90 | /* meta bits for table */ 91 | #define M 020 /* meta character; don't pass through */ 92 | #define B 040 /* cause a break */ 93 | #define MB M|B /* meta-break */ 94 | 95 | #define TRUE 1 96 | #define FALSE 0 97 | 98 | #define tTd(flag, level) (tTdvect[flag] >= (u_char)level) 99 | #define tTdlevel(flag) (tTdvect[flag]) 100 | 101 | /* variables */ 102 | extern u_char tTdvect[100]; /* trace vector */ 103 | 104 | typedef int bool; 105 | 106 | #ifndef SIZE_T 107 | # define SIZE_T size_t 108 | #endif /* ! SIZE_T */ 109 | 110 | typedef struct envelope ENVELOPE; 111 | 112 | ENVELOPE *CurEnv; /* envelope currently being processed */ 113 | 114 | /* This is a very simplified representation of the envelope structure of Sendmail */ 115 | struct envelope 116 | { 117 | char *e_to; /* the target person */ 118 | ENVELOPE *e_parent; /* the message this one encloses */ 119 | char *e_macro[MAXMACROID + 1]; /* macro definitions */ 120 | }; 121 | 122 | /* Simplified address struct */ 123 | struct address 124 | { 125 | char *q_paddr; /* the printname for the address */ 126 | char *q_user; /* user name */ 127 | char *q_ruser; /* real user name, or NULL if q_user */ 128 | char *q_host; /* host name */ 129 | char *q_home; /* home dir (local mailer only) */ 130 | char *q_fullname; /* full name if known */ 131 | struct address *q_next; /* chain */ 132 | struct address *q_alias; /* address this results from */ 133 | char *q_owner; /* owner of q_alias */ 134 | struct address *q_tchain; /* temporary use chain */ 135 | char *q_orcpt; /* ORCPT parameter from RCPT TO: line */ 136 | char *q_status; /* status code for DSNs */ 137 | char *q_rstatus; /* remote status message for DSNs */ 138 | char *q_statmta; /* MTA generating q_rstatus */ 139 | short q_state; /* address state, see below */ 140 | short q_specificity; /* how "specific" this address is */ 141 | }; 142 | 143 | typedef struct address ADDRESS; 144 | 145 | 146 | static short StateTab[NSTATES][NSTATES] = 147 | { 148 | /* oldst chtype> OPR ATM QST SPC ONE ILL */ 149 | /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, 150 | /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, 151 | /*QST*/ { QST, QST, OPR, QST, QST, QST }, 152 | /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, 153 | /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, 154 | /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M }, 155 | }; 156 | 157 | /* token type table -- it gets modified with $o characters */ 158 | static u_char TokTypeTab[256] = 159 | { 160 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 161 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 162 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 163 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 164 | /* sp ! " # $ % & ' ( ) * + , - . / */ 165 | SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 166 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 167 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 168 | /* @ A B C D E F G H I J K L M N O */ 169 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 170 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 171 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 172 | /* ` a b c d e f g h i j k l m n o */ 173 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 174 | /* p q r s t u v w x y z { | } ~ del */ 175 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 176 | 177 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 178 | OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 179 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 180 | OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 181 | /* sp ! " # $ % & ' ( ) * + , - . / */ 182 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 183 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 184 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 185 | /* @ A B C D E F G H I J K L M N O */ 186 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 187 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 188 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 189 | /* ` a b c d e f g h i j k l m n o */ 190 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 191 | /* p q r s t u v w x y z { | } ~ del */ 192 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 193 | }; 194 | 195 | /* token type table: don't strip comments */ 196 | u_char TokTypeNoC[256] = 197 | { 198 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 199 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 200 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 201 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 202 | /* sp ! " # $ % & ' ( ) * + , - . / */ 203 | SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM, 204 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 205 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 206 | /* @ A B C D E F G H I J K L M N O */ 207 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 208 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 209 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 210 | /* ` a b c d e f g h i j k l m n o */ 211 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 212 | /* p q r s t u v w x y z { | } ~ del */ 213 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 214 | 215 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 216 | OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 217 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 218 | OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 219 | /* sp ! " # $ % & ' ( ) * + , - . / */ 220 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 221 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 222 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 223 | /* @ A B C D E F G H I J K L M N O */ 224 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 225 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 226 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 227 | /* ` a b c d e f g h i j k l m n o */ 228 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 229 | /* p q r s t u v w x y z { | } ~ del */ 230 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 231 | }; 232 | 233 | #define NOCHAR -1 /* signal nothing in lookahead token */ 234 | 235 | #define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ 236 | 237 | char *OperatorChars; /* operators (old $o macro) */ 238 | int ConfigLevel; /* config file level */ 239 | 240 | 241 | /* 242 | ** PRESCAN -- Prescan name and make it canonical 243 | ** 244 | ** Scans a name and turns it into a set of tokens. This process 245 | ** deletes blanks and comments (in parentheses) (if the token type 246 | ** for left paren is SPC). 247 | ** 248 | ** This routine knows about quoted strings and angle brackets. 249 | ** 250 | ** There are certain subtleties to this routine. The one that 251 | ** comes to mind now is that backslashes on the ends of names 252 | ** are silently stripped off; this is intentional. The problem 253 | ** is that some versions of sndmsg (like at LBL) set the kill 254 | ** character to something other than @ when reading addresses; 255 | ** so people type "csvax.eric\@berkeley" -- which screws up the 256 | ** berknet mailer. 257 | ** 258 | ** Parameters: 259 | ** addr -- the name to chomp. 260 | ** delim -- the delimiter for the address, normally 261 | ** '\0' or ','; \0 is accepted in any case. 262 | ** If '\t' then we are reading the .cf file. 263 | ** pvpbuf -- place to put the saved text -- note that 264 | ** the pointers are static. 265 | ** pvpbsize -- size of pvpbuf. 266 | ** delimptr -- if non-NULL, set to the location of the 267 | ** terminating delimiter. 268 | ** toktab -- if set, a token table to use for parsing. 269 | ** If NULL, use the default table. 270 | ** 271 | ** Returns: 272 | ** A pointer to a vector of tokens. 273 | ** NULL on error. 274 | */ 275 | 276 | char ** 277 | prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) 278 | char *addr; 279 | int delim; 280 | char pvpbuf[]; 281 | int pvpbsize; 282 | char **delimptr; 283 | u_char *toktab; 284 | { 285 | register char *p; 286 | register char *q; 287 | register int c; 288 | 289 | bool bslashmode; 290 | bool route_syntax; 291 | int cmntcnt; 292 | int anglecnt; 293 | char *tok; 294 | int state; 295 | int newstate; 296 | char *saveto = CurEnv->e_to; 297 | /*static char *av[MAXATOM + 1]; */ 298 | /*static char firsttime = FALSE; */ 299 | int errno; 300 | 301 | 302 | if (toktab == NULL) 303 | toktab = TokTypeTab; 304 | 305 | /* make sure error messages don't have garbage on them */ 306 | errno = 0; 307 | 308 | q = pvpbuf; 309 | bslashmode = FALSE; 310 | route_syntax = FALSE; 311 | cmntcnt = 0; 312 | anglecnt = 0; 313 | 314 | /* avp = av; */ 315 | 316 | state = ATM; 317 | c = NOCHAR; 318 | p = addr; 319 | CurEnv->e_to = p; 320 | 321 | do 322 | { 323 | /* read a token */ 324 | tok = q; 325 | for (;;) 326 | { 327 | /* store away any old lookahead character */ 328 | if (c != NOCHAR && !bslashmode) 329 | { 330 | /* see if there is room */ 331 | 332 | if (q >= &pvpbuf[pvpbsize - 5]) 333 | { 334 | printf("553 5.1.1 Address too long\n"); 335 | 336 | if (strlen(addr) > (SIZE_T) MAXNAME) 337 | { 338 | addr[MAXNAME] = '\0'; 339 | } 340 | returnnull: 341 | if (delimptr != NULL) 342 | *delimptr = p; 343 | CurEnv->e_to = saveto; 344 | return NULL; 345 | } 346 | 347 | /* squirrel it away */ 348 | *q++ = c; 349 | } 350 | 351 | /* read a new input character */ 352 | c = *p++; 353 | if (c == '\0') 354 | { 355 | /* diagnose and patch up bad syntax */ 356 | if (state == QST) 357 | { 358 | printf("653 Unbalanced '\"'"); 359 | c = '"'; 360 | } 361 | else if (cmntcnt > 0) 362 | { 363 | printf("653 Unbalanced '('"); 364 | c = ')'; 365 | } 366 | else if (anglecnt > 0) 367 | { 368 | c = '>'; 369 | printf("653 Unbalanced '<'"); 370 | } 371 | else 372 | break; 373 | 374 | p--; 375 | } 376 | else if (c == delim && cmntcnt <= 0 && state != QST) 377 | { 378 | if (anglecnt <= 0) 379 | break; 380 | 381 | /* special case for better error management */ 382 | if (delim == ',' && !route_syntax) 383 | { 384 | printf("653 Unbalanced '<'"); 385 | c = '>'; 386 | p--; 387 | } 388 | } 389 | 390 | /* chew up special characters */ 391 | 392 | *q = '\0'; 393 | 394 | if (bslashmode) 395 | { 396 | 397 | /*printf("bslashmode = TRUE!!!\n");*/ 398 | 399 | bslashmode = FALSE; 400 | 401 | /* kludge \! for naive users */ 402 | if (cmntcnt > 0) 403 | { 404 | c = NOCHAR; 405 | continue; 406 | } 407 | else if (c != '!' || state == QST) 408 | { 409 | 410 | *q++ = '\\'; 411 | continue; /* continue while loop */ 412 | } 413 | } 414 | 415 | if (c == '\\') 416 | { 417 | bslashmode = TRUE; 418 | } 419 | else if (state == QST) 420 | { 421 | /* EMPTY */ 422 | /* do nothing, just avoid next clauses */ 423 | } 424 | else if (c == '(' && toktab['('] == SPC) 425 | { 426 | cmntcnt++; 427 | c = NOCHAR; 428 | } 429 | else if (c == ')' && toktab['('] == SPC) 430 | { 431 | if (cmntcnt <= 0) 432 | { 433 | printf("653 Unbalanced ')'"); 434 | c = NOCHAR; 435 | } 436 | else 437 | cmntcnt--; 438 | } 439 | else if (cmntcnt > 0) 440 | { 441 | c = NOCHAR; 442 | } 443 | else if (c == '<') 444 | { 445 | char *ptr = p; 446 | 447 | anglecnt++; 448 | while (isascii((int)*ptr) && isspace((int)*ptr)) 449 | ptr++; 450 | if (*ptr == '@') 451 | route_syntax = TRUE; 452 | } 453 | else if (c == '>') 454 | { 455 | if (anglecnt <= 0) 456 | { 457 | printf("653 Unbalanced '>'"); 458 | c = NOCHAR; 459 | } 460 | else{ 461 | anglecnt--; 462 | } 463 | route_syntax = FALSE; 464 | } 465 | else if (delim == ' ' && isascii(c) && isspace(c)) 466 | c = ' '; 467 | 468 | if (c == NOCHAR){ 469 | continue; 470 | } 471 | 472 | /* see if this is end of input */ 473 | if (c == delim && anglecnt <= 0 && state != QST) 474 | { 475 | break; 476 | } 477 | 478 | /*printf("oldstate = %d\n", newstate); */ 479 | newstate = StateTab[state][toktab[c & 0xff]]; 480 | /*printf("newstate = %d\n", newstate); */ 481 | 482 | state = newstate & TYPE; 483 | if (state == ILL) 484 | { 485 | if (isascii(c) && isprint(c)) 486 | printf("653 Illegal character %c", c); 487 | else 488 | printf("653 Illegal character 0x%02x", c); 489 | } 490 | /* if (bitset(M, newstate)) */ 491 | if (newstate & M) /* replacement for bitset */ { 492 | c = NOCHAR; 493 | } 494 | /* if (bitset(B, newstate)) */ 495 | if (newstate & B) 496 | { 497 | break; 498 | } 499 | } 500 | 501 | if (tok != q) 502 | { 503 | *q++ = '\0'; 504 | 505 | if (q - tok > MAXNAME) 506 | { 507 | printf("553 5.1.0 prescan: token too long"); 508 | goto returnnull; 509 | } 510 | } 511 | 512 | 513 | 514 | 515 | } while (c != '\0' && (c != delim || anglecnt > 0)); 516 | 517 | 518 | p--; 519 | if (delimptr != NULL) 520 | *delimptr = p; 521 | 522 | CurEnv->e_to = saveto; 523 | 524 | return NULL; 525 | } 526 | 527 | char ** 528 | parseaddr(addr, delim, delimptr) 529 | char *addr; 530 | int delim; 531 | char **delimptr; 532 | { 533 | 534 | register char **pvp; 535 | char pvpbuf[PSBUFSIZE]; 536 | static char *delimptrbuf; 537 | 538 | /* 539 | ** Initialize and prescan address. 540 | */ 541 | 542 | 543 | if (delimptr == NULL) 544 | delimptr = &delimptrbuf; 545 | 546 | pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL); 547 | 548 | return pvp; 549 | 550 | 551 | } 552 | 553 | int main(){ 554 | 555 | char *addr; 556 | int delim; 557 | const int MAX_MESSAGE_SIZE = 1000; 558 | int size; 559 | 560 | static char **delimptr; 561 | 562 | /* This address is valid */ 563 | /* "Misha Zitser " */ 564 | 565 | delim = '\0'; 566 | delimptr = NULL; 567 | 568 | OperatorChars = NULL; 569 | 570 | ConfigLevel = 5; 571 | 572 | CurEnv = (ENVELOPE *) malloc(sizeof(struct envelope)); 573 | CurEnv->e_to = (char *) malloc(MAX_MESSAGE_SIZE + 1); 574 | 575 | // Read from stdin 576 | addr = (char *) calloc(1,MAX_MESSAGE_SIZE+1); 577 | #ifdef __AFL_HAVE_MANUAL_CONTROL 578 | __AFL_INIT(); 579 | while (__AFL_LOOP(1000)) { 580 | 581 | #endif 582 | size = read(STDIN_FILENO, addr, MAX_MESSAGE_SIZE); 583 | if (size == -1) { 584 | printf("Failed to read from stdin\n"); 585 | return(-1); 586 | } 587 | memcpy(CurEnv->e_to, addr, size); 588 | CurEnv->e_to[size] = '\0'; // the api requires a C string 589 | 590 | parseaddr(addr, delim, delimptr); 591 | #ifdef __AFL_HAVE_MANUAL_CONTROL 592 | } 593 | #endif 594 | 595 | return 0; 596 | } 597 | 598 | 599 | /* 600 | 601 | 602 | 603 | */ 604 | 605 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/prescan-overflow-bad-start.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | MIT Copyright Notice 5 | 6 | Copyright 2003 M.I.T. 7 | 8 | Permission is hereby granted, without written agreement or royalty fee, to use, 9 | copy, modify, and distribute this software and its documentation for any 10 | purpose, provided that the above copyright notice and the following three 11 | paragraphs appear in all copies of this software. 12 | 13 | IN NO EVENT SHALL M.I.T. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 14 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE 15 | AND ITS DOCUMENTATION, EVEN IF M.I.T. HAS BEEN ADVISED OF THE POSSIBILITY OF 16 | SUCH DAMANGE. 17 | 18 | M.I.T. SPECIFICALLY DISCLAIMS ANY WARRANTIES INCLUDING, BUT NOT LIMITED TO 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 20 | AND NON-INFRINGEMENT. 21 | 22 | THE SOFTWARE IS PROVIDED ON AN "AS-IS" BASIS AND M.I.T. HAS NO OBLIGATION TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | 25 | $Author: tleek $ 26 | $Date: 2004/01/05 17:27:45 $ 27 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s5/prescan-overflow-bad.c,v 1.1.1.1 2004/01/05 17:27:45 tleek Exp $ 28 | 29 | 30 | 31 | */ 32 | 33 | 34 | /* 35 | 36 | Sendmail Copyright Notice 37 | 38 | 39 | Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 40 | All rights reserved. 41 | Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 42 | Copyright (c) 1988, 1993 43 | The Regents of the University of California. All rights reserved. 44 | 45 | By using this file, you agree to the terms and conditions set 46 | forth in the LICENSE file which can be found at the top level of 47 | the sendmail distribution. 48 | 49 | 50 | $Author: tleek $ 51 | $Date: 2004/01/05 17:27:45 $ 52 | $Header: /mnt/leo2/cvs/sabo/hist-040105/sendmail/s5/prescan-overflow-bad.c,v 1.1.1.1 2004/01/05 17:27:45 tleek Exp $ 53 | 54 | 55 | 56 | */ 57 | 58 | 59 | /* 60 | 61 | 62 | 63 | */ 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | /* states and character types */ 72 | #define OPR 0 /* operator */ 73 | #define ATM 1 /* atom */ 74 | #define QST 2 /* in quoted string */ 75 | #define SPC 3 /* chewing up spaces */ 76 | #define ONE 4 /* pick up one character */ 77 | #define ILL 5 /* illegal character */ 78 | 79 | #define NSTATES 6 /* number of states */ 80 | 81 | #define MAXNAME 40 /* max length of a name */ 82 | #define MAXATOM 10 /* max atoms per address */ 83 | #define PSBUFSIZE (MAXNAME + MAXATOM) 84 | 85 | #define MAXMACROID 0377 /* max macro id number */ 86 | 87 | #define TYPE 017 /* mask to select state type */ 88 | 89 | /* meta bits for table */ 90 | #define M 020 /* meta character; don't pass through */ 91 | #define B 040 /* cause a break */ 92 | #define MB M|B /* meta-break */ 93 | 94 | #define TRUE 1 95 | #define FALSE 0 96 | 97 | #define tTd(flag, level) (tTdvect[flag] >= (u_char)level) 98 | #define tTdlevel(flag) (tTdvect[flag]) 99 | 100 | /* variables */ 101 | extern u_char tTdvect[100]; /* trace vector */ 102 | 103 | typedef int bool; 104 | 105 | #ifndef SIZE_T 106 | # define SIZE_T size_t 107 | #endif /* ! SIZE_T */ 108 | 109 | typedef struct envelope ENVELOPE; 110 | 111 | ENVELOPE *CurEnv; /* envelope currently being processed */ 112 | 113 | /* This is a very simplified representation of the envelope structure of Sendmail */ 114 | struct envelope 115 | { 116 | char *e_to; /* the target person */ 117 | ENVELOPE *e_parent; /* the message this one encloses */ 118 | char *e_macro[MAXMACROID + 1]; /* macro definitions */ 119 | }; 120 | 121 | /* Simplified address struct */ 122 | struct address 123 | { 124 | char *q_paddr; /* the printname for the address */ 125 | char *q_user; /* user name */ 126 | char *q_ruser; /* real user name, or NULL if q_user */ 127 | char *q_host; /* host name */ 128 | char *q_home; /* home dir (local mailer only) */ 129 | char *q_fullname; /* full name if known */ 130 | struct address *q_next; /* chain */ 131 | struct address *q_alias; /* address this results from */ 132 | char *q_owner; /* owner of q_alias */ 133 | struct address *q_tchain; /* temporary use chain */ 134 | char *q_orcpt; /* ORCPT parameter from RCPT TO: line */ 135 | char *q_status; /* status code for DSNs */ 136 | char *q_rstatus; /* remote status message for DSNs */ 137 | char *q_statmta; /* MTA generating q_rstatus */ 138 | short q_state; /* address state, see below */ 139 | short q_specificity; /* how "specific" this address is */ 140 | }; 141 | 142 | typedef struct address ADDRESS; 143 | 144 | 145 | static short StateTab[NSTATES][NSTATES] = 146 | { 147 | /* oldst chtype> OPR ATM QST SPC ONE ILL */ 148 | /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, 149 | /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, 150 | /*QST*/ { QST, QST, OPR, QST, QST, QST }, 151 | /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, 152 | /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, 153 | /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M }, 154 | }; 155 | 156 | /* token type table -- it gets modified with $o characters */ 157 | static u_char TokTypeTab[256] = 158 | { 159 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 160 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 161 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 162 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 163 | /* sp ! " # $ % & ' ( ) * + , - . / */ 164 | SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 165 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 166 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 167 | /* @ A B C D E F G H I J K L M N O */ 168 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 169 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 170 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 171 | /* ` a b c d e f g h i j k l m n o */ 172 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 173 | /* p q r s t u v w x y z { | } ~ del */ 174 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 175 | 176 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 177 | OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 178 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 179 | OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 180 | /* sp ! " # $ % & ' ( ) * + , - . / */ 181 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 182 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 183 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 184 | /* @ A B C D E F G H I J K L M N O */ 185 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 186 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 187 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 188 | /* ` a b c d e f g h i j k l m n o */ 189 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 190 | /* p q r s t u v w x y z { | } ~ del */ 191 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 192 | }; 193 | 194 | /* token type table: don't strip comments */ 195 | u_char TokTypeNoC[256] = 196 | { 197 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 198 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 199 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 200 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 201 | /* sp ! " # $ % & ' ( ) * + , - . / */ 202 | SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM, 203 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 204 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 205 | /* @ A B C D E F G H I J K L M N O */ 206 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 207 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 208 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 209 | /* ` a b c d e f g h i j k l m n o */ 210 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 211 | /* p q r s t u v w x y z { | } ~ del */ 212 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 213 | 214 | /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 215 | OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 216 | /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 217 | OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 218 | /* sp ! " # $ % & ' ( ) * + , - . / */ 219 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 220 | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 221 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 222 | /* @ A B C D E F G H I J K L M N O */ 223 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 224 | /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 225 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 226 | /* ` a b c d e f g h i j k l m n o */ 227 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 228 | /* p q r s t u v w x y z { | } ~ del */ 229 | ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 230 | }; 231 | 232 | #define NOCHAR -1 /* signal nothing in lookahead token */ 233 | 234 | #define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ 235 | 236 | char *OperatorChars; /* operators (old $o macro) */ 237 | int ConfigLevel; /* config file level */ 238 | 239 | 240 | /* 241 | ** PRESCAN -- Prescan name and make it canonical 242 | ** 243 | ** Scans a name and turns it into a set of tokens. This process 244 | ** deletes blanks and comments (in parentheses) (if the token type 245 | ** for left paren is SPC). 246 | ** 247 | ** This routine knows about quoted strings and angle brackets. 248 | ** 249 | ** There are certain subtleties to this routine. The one that 250 | ** comes to mind now is that backslashes on the ends of names 251 | ** are silently stripped off; this is intentional. The problem 252 | ** is that some versions of sndmsg (like at LBL) set the kill 253 | ** character to something other than @ when reading addresses; 254 | ** so people type "csvax.eric\@berkeley" -- which screws up the 255 | ** berknet mailer. 256 | ** 257 | ** Parameters: 258 | ** addr -- the name to chomp. 259 | ** delim -- the delimiter for the address, normally 260 | ** '\0' or ','; \0 is accepted in any case. 261 | ** If '\t' then we are reading the .cf file. 262 | ** pvpbuf -- place to put the saved text -- note that 263 | ** the pointers are static. 264 | ** pvpbsize -- size of pvpbuf. 265 | ** delimptr -- if non-NULL, set to the location of the 266 | ** terminating delimiter. 267 | ** toktab -- if set, a token table to use for parsing. 268 | ** If NULL, use the default table. 269 | ** 270 | ** Returns: 271 | ** A pointer to a vector of tokens. 272 | ** NULL on error. 273 | */ 274 | 275 | char ** 276 | prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) 277 | char *addr; 278 | int delim; 279 | char pvpbuf[]; 280 | int pvpbsize; 281 | char **delimptr; 282 | u_char *toktab; 283 | { 284 | register char *p; 285 | register char *q; 286 | register int c; 287 | 288 | bool bslashmode; 289 | bool route_syntax; 290 | int cmntcnt; 291 | int anglecnt; 292 | char *tok; 293 | int state; 294 | int newstate; 295 | char *saveto = CurEnv->e_to; 296 | /*static char *av[MAXATOM + 1]; */ 297 | /*static char firsttime = FALSE; */ 298 | int errno; 299 | 300 | 301 | if (toktab == NULL) 302 | toktab = TokTypeTab; 303 | 304 | /* make sure error messages don't have garbage on them */ 305 | errno = 0; 306 | 307 | q = pvpbuf; 308 | bslashmode = FALSE; 309 | route_syntax = FALSE; 310 | cmntcnt = 0; 311 | anglecnt = 0; 312 | 313 | /* avp = av; */ 314 | 315 | state = ATM; 316 | c = NOCHAR; 317 | p = addr; 318 | CurEnv->e_to = p; 319 | 320 | do 321 | { 322 | /* read a token */ 323 | tok = q; 324 | for (;;) 325 | { 326 | /* store away any old lookahead character */ 327 | if (c != NOCHAR && !bslashmode) 328 | { 329 | /* see if there is room */ 330 | 331 | if (q >= &pvpbuf[pvpbsize - 5]) 332 | { 333 | printf("553 5.1.1 Address too long\n"); 334 | 335 | if (strlen(addr) > (SIZE_T) MAXNAME) 336 | { 337 | addr[MAXNAME] = '\0'; 338 | } 339 | returnnull: 340 | if (delimptr != NULL) 341 | *delimptr = p; 342 | CurEnv->e_to = saveto; 343 | return NULL; 344 | } 345 | 346 | /* squirrel it away */ 347 | *q++ = c; 348 | } 349 | 350 | /* read a new input character */ 351 | c = *p++; 352 | if (c == '\0') 353 | { 354 | /* diagnose and patch up bad syntax */ 355 | if (state == QST) 356 | { 357 | printf("653 Unbalanced '\"'"); 358 | c = '"'; 359 | } 360 | else if (cmntcnt > 0) 361 | { 362 | printf("653 Unbalanced '('"); 363 | c = ')'; 364 | } 365 | else if (anglecnt > 0) 366 | { 367 | c = '>'; 368 | printf("653 Unbalanced '<'"); 369 | } 370 | else 371 | break; 372 | 373 | p--; 374 | } 375 | else if (c == delim && cmntcnt <= 0 && state != QST) 376 | { 377 | if (anglecnt <= 0) 378 | break; 379 | 380 | /* special case for better error management */ 381 | if (delim == ',' && !route_syntax) 382 | { 383 | printf("653 Unbalanced '<'"); 384 | c = '>'; 385 | p--; 386 | } 387 | } 388 | 389 | /* chew up special characters */ 390 | 391 | *q = '\0'; 392 | 393 | if (bslashmode) 394 | { 395 | 396 | /*printf("bslashmode = TRUE!!!\n");*/ 397 | 398 | bslashmode = FALSE; 399 | 400 | /* kludge \! for naive users */ 401 | if (cmntcnt > 0) 402 | { 403 | c = NOCHAR; 404 | continue; 405 | } 406 | else if (c != '!' || state == QST) 407 | { 408 | 409 | *q++ = '\\'; 410 | continue; /* continue while loop */ 411 | } 412 | } 413 | 414 | if (c == '\\') 415 | { 416 | bslashmode = TRUE; 417 | } 418 | else if (state == QST) 419 | { 420 | /* EMPTY */ 421 | /* do nothing, just avoid next clauses */ 422 | } 423 | else if (c == '(' && toktab['('] == SPC) 424 | { 425 | cmntcnt++; 426 | c = NOCHAR; 427 | } 428 | else if (c == ')' && toktab['('] == SPC) 429 | { 430 | if (cmntcnt <= 0) 431 | { 432 | printf("653 Unbalanced ')'"); 433 | c = NOCHAR; 434 | } 435 | else 436 | cmntcnt--; 437 | } 438 | else if (cmntcnt > 0) 439 | { 440 | c = NOCHAR; 441 | } 442 | else if (c == '<') 443 | { 444 | char *ptr = p; 445 | 446 | anglecnt++; 447 | while (isascii((int)*ptr) && isspace((int)*ptr)) 448 | ptr++; 449 | if (*ptr == '@') 450 | route_syntax = TRUE; 451 | } 452 | else if (c == '>') 453 | { 454 | if (anglecnt <= 0) 455 | { 456 | printf("653 Unbalanced '>'"); 457 | c = NOCHAR; 458 | } 459 | else{ 460 | anglecnt--; 461 | } 462 | route_syntax = FALSE; 463 | } 464 | else if (delim == ' ' && isascii(c) && isspace(c)) 465 | c = ' '; 466 | 467 | if (c == NOCHAR){ 468 | continue; 469 | } 470 | 471 | /* see if this is end of input */ 472 | if (c == delim && anglecnt <= 0 && state != QST) 473 | { 474 | break; 475 | } 476 | 477 | /*printf("oldstate = %d\n", newstate); */ 478 | newstate = StateTab[state][toktab[c & 0xff]]; 479 | /*printf("newstate = %d\n", newstate); */ 480 | 481 | state = newstate & TYPE; 482 | if (state == ILL) 483 | { 484 | if (isascii(c) && isprint(c)) 485 | printf("653 Illegal character %c", c); 486 | else 487 | printf("653 Illegal character 0x%02x", c); 488 | } 489 | /* if (bitset(M, newstate)) */ 490 | if (newstate & M) /* replacement for bitset */ { 491 | c = NOCHAR; 492 | } 493 | /* if (bitset(B, newstate)) */ 494 | if (newstate & B) 495 | { 496 | break; 497 | } 498 | } 499 | 500 | if (tok != q) 501 | { 502 | *q++ = '\0'; 503 | 504 | if (q - tok > MAXNAME) 505 | { 506 | printf("553 5.1.0 prescan: token too long"); 507 | goto returnnull; 508 | } 509 | } 510 | 511 | 512 | 513 | 514 | } while (c != '\0' && (c != delim || anglecnt > 0)); 515 | 516 | p--; 517 | if (delimptr != NULL) 518 | *delimptr = p; 519 | 520 | CurEnv->e_to = saveto; 521 | 522 | return NULL; 523 | } 524 | 525 | char ** 526 | parseaddr(addr, delim, delimptr) 527 | char *addr; 528 | int delim; 529 | char **delimptr; 530 | { 531 | 532 | register char **pvp; 533 | char pvpbuf[PSBUFSIZE]; 534 | static char *delimptrbuf; 535 | 536 | /* 537 | ** Initialize and prescan address. 538 | */ 539 | 540 | 541 | if (delimptr == NULL) 542 | delimptr = &delimptrbuf; 543 | 544 | pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL); 545 | 546 | return pvp; 547 | 548 | 549 | } 550 | 551 | int main(){ 552 | 553 | char *addr; 554 | int delim; 555 | 556 | static char **delimptr; 557 | char special_char = '\377'; /* same char as 0xff. this char will get interpreted as NOCHAR */ 558 | int i = 0; 559 | 560 | addr = (char *) malloc(sizeof(char) * 500); 561 | strcpy(addr, "Misha Zitser "); 562 | 563 | delim = '\0'; 564 | delimptr = NULL; 565 | 566 | OperatorChars = NULL; 567 | 568 | ConfigLevel = 5; 569 | 570 | CurEnv = (ENVELOPE *) malloc(sizeof(struct envelope)); 571 | CurEnv->e_to = (char *) malloc(strlen(addr) * sizeof(char) + 1); 572 | 573 | strcpy(CurEnv->e_to, addr); 574 | 575 | parseaddr(addr, delim, delimptr); 576 | 577 | return 0; 578 | } 579 | 580 | 581 | /* 582 | 583 | 584 | 585 | */ 586 | 587 | -------------------------------------------------------------------------------- /challenges/sendmail/1305/prescan-overflow-bad.c: -------------------------------------------------------------------------------- 1 | prescan-overflow-bad-start.c -------------------------------------------------------------------------------- /environment/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | LABEL maintainer="Michael Macnair" 3 | 4 | RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections 5 | 6 | # We want manpages in the container - the base image excludes them 7 | RUN rm /etc/dpkg/dpkg.cfg.d/excludes 8 | 9 | # llvm-11 10 | RUN apt-get update && apt-get install -y --no-install-recommends wget ca-certificates gnupg2 && rm -rf /var/lib/apt/lists 11 | RUN echo deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main >> /etc/apt/sources.list 12 | RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 13 | 14 | # Packages 15 | ############## 16 | # by line: 17 | # build and afl 18 | # llvm-11 (for afl-clang-lto) 19 | # date challenge 20 | # libxml2 challenge 21 | # server/entrypoint 22 | # user tools 23 | # debugging tools 24 | RUN apt-get update && apt-get install -y \ 25 | git build-essential curl libssl-dev sudo libtool libtool-bin libglib2.0-dev bison flex automake python3 python3-dev python3-setuptools python-is-python3 libpixman-1-dev gcc-9-plugin-dev cgroup-tools \ 26 | clang-11 clang-tools-11 libc++1-11 libc++-11-dev libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev libomp5-11 lld-11 lldb-11 python3-lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools \ 27 | rsync autopoint bison gperf autoconf texinfo gettext \ 28 | libtool pkg-config libz-dev python2.7-dev \ 29 | awscli openssh-server ncat \ 30 | emacs vim nano screen htop man manpages-posix-dev wget httpie bash-completion ripgrep \ 31 | gdb byobu \ 32 | && rm -rf /var/lib/apt/lists 33 | 34 | RUN echo y | unminimize 35 | 36 | # Users & SSH 37 | ############## 38 | RUN useradd --create-home --shell /bin/bash fuzzer 39 | # See the README - the password is set by the entry script 40 | 41 | # passwordless sudo access for ASAN and installing extra tools: 42 | RUN echo "fuzzer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 43 | # RUN usermod -aG sudo fuzzer 44 | 45 | RUN mkdir /var/run/sshd 46 | 47 | # AFL 48 | ########### 49 | RUN update-alternatives --install /usr/bin/clang clang $(which clang-11) 1 && \ 50 | update-alternatives --install /usr/bin/clang++ clang++ $(which clang++-11) 1 && \ 51 | update-alternatives --install /usr/bin/llvm-config llvm-config $(which llvm-config-11) 1 && \ 52 | update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer $(which llvm-symbolizer-11) 1 && \ 53 | update-alternatives --install /usr/bin/llvm-cov llvm-cov $(which llvm-cov-11) 1 && \ 54 | update-alternatives --install /usr/bin/llvm-profdata llvm-profdata $(which llvm-profdata-11) 1 55 | 56 | # (environment variables won't be visible in the SSH session unless added to /etc/profile or similar) 57 | ENV AFLVERSION=3.0c 58 | USER fuzzer 59 | WORKDIR /home/fuzzer 60 | RUN git clone https://github.com/AFLplusplus/AFLplusplus 61 | WORKDIR /home/fuzzer/AFLplusplus 62 | RUN git checkout $AFLVERSION 63 | RUN make distrib 64 | RUN sudo make install 65 | 66 | # You could install gnuplot-nox, but it increases the image size a lot (~100 extra packages). 67 | # Students can install it themselves if they want it. 68 | 69 | # Exercises 70 | ############## 71 | USER fuzzer 72 | WORKDIR /home/fuzzer 73 | RUN git clone https://github.com/mykter/afl-training.git workshop 74 | # Use this if building using a local copy of the training materials 75 | # ADD . ./local-workshop 76 | # USER root 77 | # RUN chown -R fuzzer:fuzzer local-workshop 78 | 79 | WORKDIR /home/fuzzer/workshop 80 | RUN http https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.2.tar.gz | tar xz -C challenges/ntpq 81 | RUN http https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.8p10.tar.gz | tar xz -C challenges/ntpq 82 | RUN git submodule init && git submodule update 83 | 84 | # By default run an SSH daemon. To run locally instead, use something like this: 85 | # docker run -it --user fuzzer afl-training:latest /bin/bash 86 | ############## 87 | USER root 88 | 89 | COPY entrypoint.sh /usr/local/bin/entrypoint.sh 90 | 91 | EXPOSE 22 92 | CMD ["entrypoint.sh"] 93 | # on some systems you might want to run AFLplusplus/afl-system-config; only works if the container is run in privileged mode and you don't care about security of the host 94 | -------------------------------------------------------------------------------- /environment/README.md: -------------------------------------------------------------------------------- 1 | This Dockerfile produces a docker image set up ready for the training. 2 | 3 | # Building 4 | 5 | Optional, as the image is available at 6 | [ghcr.io/mykter/fuzz-training](http://ghcr.io/mykter/fuzz-training). 7 | 8 | $ docker build . -t fuzz-training 9 | 10 | # Running Locally 11 | 12 | $ docker run --privileged -p 2222:2222 -e PASSMETHOD=env -e PASS= ghcr.io/mykter/fuzz-training 13 | $ ssh fuzzer@localhost -p 2222 14 | 15 | You need to use a privileged container to use the `asan_cgroups/limit_memory.sh` script and to use debuggers like gdb. 16 | 17 | # Running in the cloud 18 | 19 | There are lots of ways to run containers in the cloud! 20 | 21 | Prior to the GrayHat conference, I used a single large VM with multiple instances of the container running on it. This 22 | caused performance and management complications though, so now use a single VM per student. See the history of this file 23 | for guidance on that approach on AWS. 24 | 25 | Currently this document describes running on GCP, and assumes you have configured a default project, region, and zone. 26 | 27 | ## SSH Credentials 28 | 29 | Students each get their own container, which they can SSH in to as the `fuzzer` user. The password for this user can 30 | either be set through an environment variable, dynamically retrieved from AWS SSM Parameter Store, or randomly generated 31 | on startup and reported back to a listening host. See the `entrypoint.sh` script for details. 32 | 33 | ## VMs 34 | 35 | First create a VPC with a public subnet, and a firewall rule that allows inbound ssh (22). 36 | 37 | Create a template for an n2-standard-4 which gives a student 4 cores & 16gb RAM for ~\$0.2/hour. The template can 38 | specify the VM should use a container OS and run the container, which is a lovely feature. 39 | 40 | You need privileged containers for CAP_PTRACE for debugging and the necessary cgroup privileges to use the 41 | `asan_cgroups/limit_memory.sh` script. It's still a viable option to not use privileged containers - ASAN usually works 42 | with `-m none` and `ASAN_OPTIONS=detect_leaks=0`, and debugging is not the focus of the workshop. 43 | 44 | We have to configure the container's SSH daemon to listen on a non-default port, as otherwise it conflicts with the 45 | host's. 46 | 47 | $ gcloud compute networks create fuzz-training --subnet-mode=custom 48 | $ gcloud compute networks subnets create main --network=fuzz-training --enable-flow-logs --range=10.0.0.0/24 --region=us-central1 49 | $ gcloud compute firewall-rules create allow-workshop-ssh \ 50 | --allow=tcp:2222 --direction=INGRESS --network=fuzz-training --target-tags=student 51 | $ gcloud compute instance-templates create-with-container fuzz-training \ 52 | --container-image ghcr.io/mykter/fuzz-training \ 53 | --container-privileged \ 54 | --container-env PASSMETHOD=,SSHPORT=2222,SYSTEMCONFIG=1 \ 55 | --machine-type n2-standard-4 \ 56 | --region us-central1 \ 57 | --network fuzz-training --subnet main \ 58 | --no-service-account --no-scopes \ 59 | --metadata enable-guest-attributes=TRUE \ 60 | --tags student \ 61 | --description "4 core machine running the afl training workshop container image in privileged mode, ssh listening on port 2222" 62 | 63 | When creating the instance template you need to specify how to set the password. There are various options, each section 64 | below describes one. 65 | 66 | ## Individually created VMs 67 | 68 | To fix a static password for all instances, use: 69 | 70 | --container-env PASSMETHOD=env,PASS=secret,... 71 | 72 | Then try something like this: 73 | 74 | $ gcloud compute instances create fuzz-training-{} --zone=us-central1-a --source-instance-template=fuzz-training 75 | 76 | ## Facilitator-managed VMs 77 | 78 | As a facilitator you can trigger the creation of a number of VMs and have them generate and send their IP address and 79 | SSH password to you, for you to forward on to students. 80 | 81 | Create a tiny instance and a firewall rule that allows internal inbound connections on a port such as 1234: 82 | 83 | $ gcloud compute firewall-rules create allow-workshop-pass \ 84 | --allow=tcp:1234 --direction=INGRESS --network=fuzz-training --target-tags=controller --source-tags=student 85 | $ gcloud compute instances create controller \ 86 | --machine-type=e2-micro --no-service-account --no-scopes \ 87 | --network-interface=private-network-ip=10.13.37.2,network=fuzz-training,subnet=main \ 88 | --tags=controller \ 89 | --description="password manager" 90 | 91 | When creating the instance template, use: 92 | 93 | --container-env PASSMETHOD=callback,PASSHOST=,PASSPORT=1234,... 94 | 95 | Spin up some instances (number defined in the brace expansion at the end). 96 | 97 | $ parallel --verbose --keep-order "gcloud compute instances create fuzz-training-{} --zone=us-central1-a --source-instance-template=fuzz-training" ::: {1..2} 98 | 99 | ## Self-service VMs 100 | 101 | With larger groups you can offer a self-service option via a simple website hosted on Cloud Run, that allows students to 102 | provision their own machine. Note it's unauthenticated and hence open to abuse! You should shut it down once the session 103 | is over or everyone has a VM. 104 | 105 | Create the template with: 106 | 107 | --container-env PASSMETHOD=gcpmeta,... 108 | 109 | Create a service account that has the Compute Admin role (or at least enough permissions to create VMs and read their 110 | metadata), and then deploy a Cloud Run service: 111 | 112 | $ cd self-serve && docker build . -t gcr.io//fuzz-training-provisioner && docker push gcr.io//fuzz-training-provisioner 113 | $ export KEY=$(head -c 32 /dev/urandom | base64) 114 | $ gcloud run deploy fuzz-training-provisioner --image=gcr.io/myproj/fuzz-training-provisioner \ 115 | --allow-unauthenticated --max-instances=1 \ 116 | --set-env-vars=PROJECT=my-gcp-proj,ZONE=us-central1-a,TEMPL=fuzz-training,COOKIE_KEY=${KEY},VMLIMIT=100,DEBUG=0 \ 117 | --service-account= 118 | 119 | Point your students at the URL the service is deployed to, and they will be able to create a VM and get the credentials 120 | for it. They can also delete their VM when they've finished with it. Note the VMLIMIT applies to all of the compute 121 | instances in the specified zone - if you have existing instances they are counted against the limit. 122 | 123 | Don't forget to delete the service when you're done: it offers free compute to anyone who can find the URL! 124 | 125 | $ gcloud run services delete fuzz-training-provisioner 126 | 127 | ## Running 128 | 129 | SSH in (with the port configured previously): 130 | 131 | $ ssh fuzzer@ -p 2222 132 | 133 | For max performance, and when doing multi-core fuzzing, we should manually specify which CPUs to bind to, because AFL 134 | can't tell which ones it has access to. In this workshop, as each student has their own VM and all of the cores on it, 135 | we don't bother with this optimization however. See Brandon Falk's excellent post on 136 | [scaling AFL to 256 threads](https://gamozolabs.github.io/fuzzing/2018/09/16/scaling_afl.html) for details (or the 137 | history of this file for how to use it). 138 | -------------------------------------------------------------------------------- /environment/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | 5 | case "$PASSMETHOD" in 6 | "env") 7 | if [[ -z $PASS ]]; then 8 | echo "env password method specified, but no password found in PASS environment variable" >&2 9 | exit 1 10 | fi 11 | echo "Password set from environment variable" 12 | ;; 13 | "awsssm") 14 | if [[ -z $PASSPARAM || -z $PASSREGION ]]; then 15 | echo "awsssm password method specified, but missing PASSPARAM or PASSREGION environment variable" >&2 16 | exit 1 17 | fi 18 | echo "Getting password from parameter $PASSPARAM" 19 | PASS=$(aws ssm get-parameter --name $PASSPARAM --with-decryption --region $PASSREGION --query 'Parameter.Value' --output text) 20 | ;; 21 | "callback") 22 | if [[ -z $PASSHOST || -z $PASSPORT ]]; then 23 | echo "callback password method specified, but missing PASSHOST or PASSPORT environment variable" >&2 24 | exit 1 25 | fi 26 | PASS=$(head -c 9 /dev/urandom | base64) 27 | IP=$(curl https://api.ipify.org) 28 | echo "$(hostname) $IP $PASS" | nc $PASSHOST $PASSPORT # network listeners get free access to our instances ¯\_(ツ)_/¯ 29 | # $PASSHOST should run something like "nc -kl $PASSPORT | tee passwords.txt" 30 | ;; 31 | "gcpmeta") 32 | PASS=$(head -c 9 /dev/urandom | base64) 33 | echo $PASS | http --check-status PUT http://metadata.google.internal/computeMetadata/v1/instance/guest-attributes/fuzzing/password Metadata-Flavor:Google 34 | ;; 35 | *) 36 | echo "You must specify a method for setting the fuzzer user's password, or use a different entrypoint." >&2 37 | echo "set the PASSMETHOD environment variable to 'env', 'awsssm', 'gcpmeta', or 'callback'" >&2 38 | exit 1 39 | ;; 40 | esac 41 | echo "fuzzer:$PASS" | chpasswd 42 | 43 | if [[ -n "$MANUALCPUS" ]]; then 44 | echo "Setting default value of AFL_NO_AFFINITY" 45 | echo "export AFL_NO_AFFINITY=1" >> /etc/profile 46 | fi 47 | echo "stty -ixon" >> /etc/profile # don't treat ctrl+s as scrolllock 48 | 49 | if [[ -n "$SYSTEMCONFIG" ]]; then 50 | set +e # some aspects of this will often fail; best-efforts is fine for a learning environment 51 | /home/fuzzer/AFLplusplus/afl-system-config 52 | fi 53 | 54 | echo "Spawning SSHd on port ${SSHPORT-2222}" 55 | /usr/sbin/sshd -D -p ${SSHPORT-2222} 56 | -------------------------------------------------------------------------------- /environment/self-serve/.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 10m 3 | tests: false 4 | issues: 5 | max-issues-per-linter: 0 6 | max-same-issues: 0 7 | linters-settings: 8 | goimports: 9 | local-prefixes: git.tesdev.io/ 10 | govet: 11 | check-shadowing: true 12 | linters: 13 | enable: 14 | - bodyclose 15 | - gosec 16 | - interfacer 17 | - unconvert 18 | - dupl 19 | - goconst 20 | - gocognit 21 | - asciicheck 22 | - gocyclo 23 | # - gofmt isn't needed - goimports replaces it 24 | - goimports # gofmt + unused imports + import clustering 25 | - misspell 26 | - govet 27 | - megacheck 28 | - gochecknoinits 29 | - whitespace 30 | - scopelint 31 | - nakedret 32 | - dogsled 33 | - unparam 34 | - nolintlint 35 | -------------------------------------------------------------------------------- /environment/self-serve/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.15 as build 2 | WORKDIR /build 3 | COPY . /build 4 | RUN CGO_ENABLED=0 go build 5 | 6 | FROM gcr.io/distroless/static 7 | COPY --from=build /build/self-serve /self-serve 8 | COPY mvp.css /mvp.css 9 | COPY favicon.ico /favicon.ico 10 | CMD ["/self-serve"] -------------------------------------------------------------------------------- /environment/self-serve/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykter/afl-training/628042ea77a1c0ada826618f77130e4b5d810231/environment/self-serve/favicon.ico -------------------------------------------------------------------------------- /environment/self-serve/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mykter/afl-training/environment/self-serve 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/gorilla/sessions v1.2.1 7 | github.com/rs/zerolog v1.20.0 8 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 9 | google.golang.org/api v0.38.0 10 | ) 11 | -------------------------------------------------------------------------------- /environment/self-serve/mvp.css: -------------------------------------------------------------------------------- 1 | /* MVP.css v1.6.3 - https://github.com/andybrewer/mvp */ 2 | 3 | :root { 4 | --border-radius: 5px; 5 | --box-shadow: 2px 2px 10px; 6 | --color: #118bee; 7 | --color-accent: #118bee15; 8 | --color-bg: #fff; 9 | --color-bg-secondary: #e9e9e9; 10 | --color-secondary: #920de9; 11 | --color-secondary-accent: #920de90b; 12 | --color-shadow: #f4f4f4; 13 | --color-text: #000; 14 | --color-text-secondary: #999; 15 | --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 16 | --hover-brightness: 1.2; 17 | --justify-important: center; 18 | --justify-normal: left; 19 | --line-height: 1.5; 20 | --width-card: 285px; 21 | --width-card-medium: 460px; 22 | --width-card-wide: 800px; 23 | --width-content: 1080px; 24 | } 25 | 26 | /* 27 | @media (prefers-color-scheme: dark) { 28 | :root { 29 | --color: #0097fc; 30 | --color-accent: #0097fc4f; 31 | --color-bg: #333; 32 | --color-bg-secondary: #555; 33 | --color-secondary: #e20de9; 34 | --color-secondary-accent: #e20de94f; 35 | --color-shadow: #bbbbbb20; 36 | --color-text: #f7f7f7; 37 | --color-text-secondary: #aaa; 38 | } 39 | } 40 | */ 41 | 42 | /* Layout */ 43 | article aside { 44 | background: var(--color-secondary-accent); 45 | border-left: 4px solid var(--color-secondary); 46 | padding: 0.01rem 0.8rem; 47 | } 48 | 49 | body { 50 | background: var(--color-bg); 51 | color: var(--color-text); 52 | font-family: var(--font-family); 53 | line-height: var(--line-height); 54 | margin: 0; 55 | overflow-x: hidden; 56 | padding: 1rem 0; 57 | } 58 | 59 | footer, 60 | header, 61 | main { 62 | margin: 0 auto; 63 | max-width: var(--width-content); 64 | padding: 2rem 1rem; 65 | } 66 | 67 | hr { 68 | background-color: var(--color-bg-secondary); 69 | border: none; 70 | height: 1px; 71 | margin: 4rem 0; 72 | } 73 | 74 | section { 75 | display: flex; 76 | flex-wrap: wrap; 77 | justify-content: var(--justify-important); 78 | } 79 | 80 | section aside { 81 | border: 1px solid var(--color-bg-secondary); 82 | border-radius: var(--border-radius); 83 | box-shadow: var(--box-shadow) var(--color-shadow); 84 | margin: 1rem; 85 | padding: 1.25rem; 86 | width: var(--width-card); 87 | } 88 | 89 | section aside:hover { 90 | box-shadow: var(--box-shadow) var(--color-bg-secondary); 91 | } 92 | 93 | section aside img { 94 | max-width: 100%; 95 | } 96 | 97 | [hidden] { 98 | display: none; 99 | } 100 | 101 | /* Headers */ 102 | article header, 103 | div header, 104 | main header { 105 | padding-top: 0; 106 | } 107 | 108 | header { 109 | text-align: var(--justify-important); 110 | } 111 | 112 | header a b, 113 | header a em, 114 | header a i, 115 | header a strong { 116 | margin-left: 0.5rem; 117 | margin-right: 0.5rem; 118 | } 119 | 120 | header nav img { 121 | margin: 1rem 0; 122 | } 123 | 124 | section header { 125 | padding-top: 0; 126 | width: 100%; 127 | } 128 | 129 | /* Nav */ 130 | nav { 131 | align-items: center; 132 | display: flex; 133 | font-weight: bold; 134 | justify-content: space-between; 135 | margin-bottom: 7rem; 136 | } 137 | 138 | nav ul { 139 | list-style: none; 140 | padding: 0; 141 | } 142 | 143 | nav ul li { 144 | display: inline-block; 145 | margin: 0 0.5rem; 146 | position: relative; 147 | text-align: left; 148 | } 149 | 150 | /* Nav Dropdown */ 151 | nav ul li:hover ul { 152 | display: block; 153 | } 154 | 155 | nav ul li ul { 156 | background: var(--color-bg); 157 | border: 1px solid var(--color-bg-secondary); 158 | border-radius: var(--border-radius); 159 | box-shadow: var(--box-shadow) var(--color-shadow); 160 | display: none; 161 | height: auto; 162 | left: -2px; 163 | padding: .5rem 1rem; 164 | position: absolute; 165 | top: 1.7rem; 166 | white-space: nowrap; 167 | width: auto; 168 | z-index: 1; 169 | } 170 | 171 | nav ul li ul::before { 172 | /* fill gap above to make mousing over them easier */ 173 | content: ""; 174 | position: absolute; 175 | left: 0; 176 | right: 0; 177 | top: -0.5rem; 178 | height: 0.5rem; 179 | } 180 | 181 | nav ul li ul li, 182 | nav ul li ul li a { 183 | display: block; 184 | } 185 | 186 | /* Typography */ 187 | code, 188 | samp { 189 | background-color: var(--color-accent); 190 | border-radius: var(--border-radius); 191 | color: var(--color-text); 192 | display: inline-block; 193 | margin: 0 0.1rem; 194 | padding: 0 0.5rem; 195 | } 196 | 197 | details { 198 | margin: 1.3rem 0; 199 | } 200 | 201 | details summary { 202 | font-weight: bold; 203 | cursor: pointer; 204 | } 205 | 206 | h1, 207 | h2, 208 | h3, 209 | h4, 210 | h5, 211 | h6 { 212 | line-height: var(--line-height); 213 | } 214 | 215 | mark { 216 | padding: 0.1rem; 217 | } 218 | 219 | ol li, 220 | ul li { 221 | padding: 0.2rem 0; 222 | } 223 | 224 | p { 225 | margin: 0.75rem 0; 226 | padding: 0; 227 | } 228 | 229 | pre { 230 | margin: 1rem 0; 231 | max-width: var(--width-card-wide); 232 | padding: 1rem 0; 233 | } 234 | 235 | pre code, 236 | pre samp { 237 | display: block; 238 | max-width: var(--width-card-wide); 239 | padding: 0.5rem 2rem; 240 | white-space: pre-wrap; 241 | } 242 | 243 | small { 244 | color: var(--color-text-secondary); 245 | } 246 | 247 | sup { 248 | background-color: var(--color-secondary); 249 | border-radius: var(--border-radius); 250 | color: var(--color-bg); 251 | font-size: xx-small; 252 | font-weight: bold; 253 | margin: 0.2rem; 254 | padding: 0.2rem 0.3rem; 255 | position: relative; 256 | top: -2px; 257 | } 258 | 259 | /* Links */ 260 | a { 261 | color: var(--color-secondary); 262 | display: inline-block; 263 | font-weight: bold; 264 | text-decoration: none; 265 | } 266 | 267 | a:hover { 268 | filter: brightness(var(--hover-brightness)); 269 | text-decoration: underline; 270 | } 271 | 272 | a b, 273 | a em, 274 | a i, 275 | a strong, 276 | button { 277 | border-radius: var(--border-radius); 278 | display: inline-block; 279 | font-size: medium; 280 | font-weight: bold; 281 | line-height: var(--line-height); 282 | margin: 0.5rem 0; 283 | padding: 1rem 2rem; 284 | } 285 | 286 | button { 287 | font-family: var(--font-family); 288 | } 289 | 290 | button:hover { 291 | cursor: pointer; 292 | filter: brightness(var(--hover-brightness)); 293 | } 294 | 295 | a b, 296 | a strong, 297 | button { 298 | background-color: var(--color); 299 | border: 2px solid var(--color); 300 | color: var(--color-bg); 301 | } 302 | 303 | a em, 304 | a i { 305 | border: 2px solid var(--color); 306 | border-radius: var(--border-radius); 307 | color: var(--color); 308 | display: inline-block; 309 | padding: 1rem 2rem; 310 | } 311 | 312 | /* Images */ 313 | figure { 314 | margin: 0; 315 | padding: 0; 316 | } 317 | 318 | figure img { 319 | max-width: 100%; 320 | } 321 | 322 | figure figcaption { 323 | color: var(--color-text-secondary); 324 | } 325 | 326 | /* Forms */ 327 | 328 | button:disabled, 329 | input:disabled { 330 | background: var(--color-bg-secondary); 331 | border-color: var(--color-bg-secondary); 332 | color: var(--color-text-secondary); 333 | cursor: not-allowed; 334 | } 335 | 336 | button[disabled]:hover { 337 | filter: none; 338 | } 339 | 340 | form { 341 | border: 1px solid var(--color-bg-secondary); 342 | border-radius: var(--border-radius); 343 | box-shadow: var(--box-shadow) var(--color-shadow); 344 | display: block; 345 | max-width: var(--width-card-wide); 346 | min-width: var(--width-card); 347 | padding: 1.5rem; 348 | text-align: var(--justify-normal); 349 | } 350 | 351 | form header { 352 | margin: 1.5rem 0; 353 | padding: 1.5rem 0; 354 | } 355 | 356 | input, 357 | label, 358 | select, 359 | textarea { 360 | display: block; 361 | font-size: inherit; 362 | max-width: var(--width-card-wide); 363 | } 364 | 365 | input[type="checkbox"], 366 | input[type="radio"] { 367 | display: inline-block; 368 | } 369 | 370 | input[type="checkbox"]+label, 371 | input[type="radio"]+label { 372 | display: inline-block; 373 | font-weight: normal; 374 | position: relative; 375 | top: 1px; 376 | } 377 | 378 | input, 379 | select, 380 | textarea { 381 | border: 1px solid var(--color-bg-secondary); 382 | border-radius: var(--border-radius); 383 | margin-bottom: 1rem; 384 | padding: 0.4rem 0.8rem; 385 | } 386 | 387 | input[readonly], 388 | textarea[readonly] { 389 | background-color: var(--color-bg-secondary); 390 | } 391 | 392 | label { 393 | font-weight: bold; 394 | margin-bottom: 0.2rem; 395 | } 396 | 397 | /* Tables */ 398 | table { 399 | border: 1px solid var(--color-bg-secondary); 400 | border-radius: var(--border-radius); 401 | border-spacing: 0; 402 | display: inline-block; 403 | max-width: 100%; 404 | overflow-x: auto; 405 | padding: 0; 406 | white-space: nowrap; 407 | } 408 | 409 | table td, 410 | table th, 411 | table tr { 412 | padding: 0.4rem 0.8rem; 413 | text-align: var(--justify-important); 414 | } 415 | 416 | table thead { 417 | background-color: var(--color); 418 | border-collapse: collapse; 419 | border-radius: var(--border-radius); 420 | color: var(--color-bg); 421 | margin: 0; 422 | padding: 0; 423 | } 424 | 425 | table thead th:first-child { 426 | border-top-left-radius: var(--border-radius); 427 | } 428 | 429 | table thead th:last-child { 430 | border-top-right-radius: var(--border-radius); 431 | } 432 | 433 | table thead th:first-child, 434 | table tr td:first-child { 435 | text-align: var(--justify-normal); 436 | } 437 | 438 | table tr:nth-child(even) { 439 | background-color: var(--color-accent); 440 | } 441 | 442 | /* Quotes */ 443 | blockquote { 444 | display: block; 445 | font-size: x-large; 446 | line-height: var(--line-height); 447 | margin: 1rem auto; 448 | max-width: var(--width-card-medium); 449 | padding: 1.5rem 1rem; 450 | text-align: var(--justify-important); 451 | } 452 | 453 | blockquote footer { 454 | color: var(--color-text-secondary); 455 | display: block; 456 | font-size: small; 457 | line-height: var(--line-height); 458 | padding: 1.5rem 0; 459 | } -------------------------------------------------------------------------------- /harness/README.md: -------------------------------------------------------------------------------- 1 | This small exercise is about writing a harness around a bit of code to allow it to be fuzzed with afl. Whilst there is a discoverable crash, the goal here is to learn about the plumbing to interface AFL with a target bit of code. 2 | 3 | If you were comfortable with what was happening when fuzzing the `vulnerable` quickstart program, and understand how afl sends data to the target program, you can skip this and move on to the challenges. 4 | 5 | This diagram shows the main components and interactions when using AFL: 6 | ![afl overview](./overview.svg) 7 | 8 | Test harness basics 9 | ------------------- 10 | 11 | The code in `library.c` manipulates some input data and gives an output. 12 | ```c 13 | // an 'nprintf' implementation - print the first len bytes of data 14 | void lib_echo(char *data, ssize_t len); 15 | 16 | // optimised multiply - returns x*y 17 | int lib_mul(int x, int y); 18 | ``` 19 | How can we fuzz it? 20 | 21 | 1. The code needs to be executable - it needs to be compiled into a program. 22 | 23 | 2. To allow AFL to work effectively, the code needs to be instrumented - so we have to compile it using one of afl-clang-fast, afl-clang, or afl-gcc. 24 | 25 | 3. For the data generated by AFL to actually test the library, we have to write a _harness_ that will take external input and feed it to the library. This can either be from a file specified on the command line, or directly from stdin. 26 | 27 | A minimal stdin test harness 28 | ---------------------------- 29 | 30 | To meet point 1 we need a `main()` function that calls the library. Here's an example we'll call `harness.c`: 31 | ```c 32 | #include "library.h" 33 | #include 34 | #include 35 | void main() { 36 | char *data = "Some input data\n"; 37 | lib_echo(data, strlen(data)); 38 | printf("%d\n", lib_mul(1,2)); 39 | } 40 | ``` 41 | 42 | We can compile this minimal program like so: 43 | 44 | `AFL_HARDEN=1 afl-clang-fast harness.c library.c -o harness` 45 | 46 | It will call the library code (run `./harness` to test it out), but there's no hook yet to allow the inputs generated by afl to make it to the target function. Try running this program under afl-fuzz: `afl-fuzz -i in -o out ./harness` - you will see that afl gives you a warning that nothing is happening: "(odd, check syntax!)". 47 | 48 | So let's make our harness take input from stdin and feed it to the target function. See `man 3 stdin` for an overview if the concept of standard input and output is new to you. 49 | 50 | ```c 51 | #include 52 | #include 53 | #include 54 | 55 | #include "library.h" 56 | 57 | // fixed size buffer based on assumptions about the maximum size that is likely necessary to exercise all aspects of the target function 58 | #define SIZE 50 59 | 60 | int main() { 61 | // make sure buffer is initialized to eliminate variable behaviour that isn't dependent on the input. 62 | char input[SIZE] = {0}; 63 | 64 | ssize_t length; 65 | length = read(STDIN_FILENO, input, SIZE); 66 | 67 | lib_echo(input, length); 68 | } 69 | ``` 70 | 71 | After compiling this with the instrumenting compiler, running it under afl-fuzz should give (marginally) better results - now the inputs it is sending to the program are actually having an impact on the execution flow, and it can discover inputs that lead to different paths. This is an incredibly simple program though, and the underlying printf call _isn't_ instrumented, so AFL won't find many paths. 72 | One of the paths leads to a crash, so if your harness is working you should soon see AFL report a crash. We won't discuss the crash further here, as we're focussed on harness writing. See the quickstart for more info on understanding and reproducing the crash. 73 | 74 | Arbitrary input formats 75 | ----------------------- 76 | Fuzzing `lib_echo` is pretty straightforward - but what about `lib_mul`? It doesn't take a buffer as input, it takes two numbers. To handle this, our harness will simply parse out two integers from the input stream. 77 | 78 | ```c 79 | #include 80 | #include 81 | #include 82 | 83 | #include "library.h" 84 | 85 | // fixed size buffer based on assumptions about the maximum size that is likely necessary to exercise all aspects of the target function 86 | #define SIZE 100 87 | 88 | int main(int argc, char* argv[]) { 89 | if((argc == 2) && strcmp(argv[1], "echo") == 0) { 90 | // make sure buffer is initialized to eliminate variable behaviour that isn't dependent on the input. 91 | char input[SIZE] = {0}; 92 | 93 | ssize_t length; 94 | length = read(STDIN_FILENO, input, SIZE); 95 | 96 | lib_echo(input, length); 97 | } else if ((argc == 2) && strcmp(argv[1], "mul") == 0) { 98 | int a,b = 0; 99 | read(STDIN_FILENO, &a, 4); 100 | read(STDIN_FILENO, &b, 4); 101 | printf("%d\n", lib_mul(a,b)); 102 | } else { 103 | printf("Usage: %s mul|echo\n", argv[0]); 104 | } 105 | } 106 | ``` 107 | 108 | We don't need to 'tell' afl-fuzz about this harness in any special way - starting from the seeds its usual algorithm will work out what makes an interesting input, just like it does for any other target. But note that as we've added some functionality to our harness to specify which library function to fuzz, we now we need to tell afl-fuzz how to launch it: 109 | 110 | afl-fuzz -i in -o out ./harness mul 111 | 112 | or 113 | 114 | afl-fuzz -i in -o out ./harness echo 115 | 116 | If instead we wanted to fuzz both functions in a single fuzzing job, then we could rework the harness to, for example, use the first 8 bytes as input to `lib_mul`, and any remaining bytes as input to `lib_echo`. 117 | 118 | 119 | Writing a file-input test harness 120 | --------------------------------- 121 | Left as an exercise, as reading from stdin is usually sufficient. The steps are: 122 | 123 | 1. Read a filename from argv 124 | 2. Open the specified file and read its contents into a buffer. 125 | 3. Pass that buffer to the target function. 126 | 127 | Aside: fuzzing for functional correctness 128 | ----------------------------------------- 129 | Consider how you might use fuzzing to test whether `lib_mul` correctly implements the standard multiply operation. This is covered later on in the course slides. 130 | -------------------------------------------------------------------------------- /harness/library.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "library.h" 7 | 8 | void lib_echo(char *data, ssize_t len){ 9 | if(strlen(data) == 0) { 10 | return; 11 | } 12 | char *buf = calloc(1, len); 13 | strncpy(buf, data, len); 14 | printf("%s",buf); 15 | free(buf); 16 | 17 | // A crash so we can tell the harness is working for lib_echo 18 | if(data[0] == 'p') { 19 | if(data[1] == 'o') { 20 | if(data[2] =='p') { 21 | if(data[3] == '!') { 22 | assert(0); 23 | } 24 | } 25 | } 26 | } 27 | } 28 | 29 | int lib_mul(int x, int y){ 30 | if(x%2 == 0) { 31 | return y << x; 32 | } else if (y%2 == 0) { 33 | return x << y; 34 | } else if (x == 0) { 35 | return 0; 36 | } else if (y == 0) { 37 | return 0; 38 | } else { 39 | return x * y; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /harness/library.h: -------------------------------------------------------------------------------- 1 | #include 2 | // an 'nprintf' implementation - print the first len bytes of data 3 | void lib_echo(char *data, ssize_t len); 4 | 5 | // optimised multiply - returns x*y 6 | int lib_mul(int x, int y); 7 | -------------------------------------------------------------------------------- /harness/overview.drawio: -------------------------------------------------------------------------------- 1 | 7Vtbc5s4FP41frSHm7F5dNJ4uzPdTnY6222fdmSQQQlGVIjYzq9fCSSDLLDjGJukrV8CByHQOd93dC5kYN+uNn8QkEZ/4QDGA8sINgP7w8CyzKnpsj9cshUSYzotJSFBgZBVgi/oGcqBQpqjAGbKQIpxTFGqCn2cJNCnigwQgtfqsCWO1aemIISa4IsPYl36LwpoVEqn1qSSf4QojOSTTdcrr6yAHCxWkkUgwOuayL4b2LcEY1oerTa3MObak3op75u3XN29GIEJfckNX1NEPyfDIA4fHt2Ps88P1t9fh6ZTTvME4lysmMKMcoWCjOmdaTsC/JQSFIaQDCw3Zk+7WfCjkB8lcM0XRwGFxTCQZIginGRi1XQrVQkDpllxigmNcIgTEN9V0huC8ySA/H0NdlaN+YRxyoQmEz5ASrcCJiCnmIkiuorFVaYKsv3G7x8ZxkQKvheCsetIwYeNeER5tq2f3UOCVpCypQrhBtFvteNyMmssTqup+Imcyc/JU7EO/lKlIvjqWy0nRBnOiQ8PmUswAJAQ0kPjKoAxakLMFkS27EbByyFbgOkKIkpmCiQQGAOKntR3BYJF4W6q3ez3GCW0mtoeG8q01thTpyjfXdxVh6ucSA7Ey2UGlTHsoLaaSlQA/RTQjzXQr3IO4ECHt8qGHhE95JA2VUhbhnUOpM33BGnrIKKNkTP1PAV5Q3P6GkTPCAHb2oCUwzJrB7wzdpXHOo65h9lyxuujfPIy164hXnp6wycgi3qCvfS6o3EdpAcR2oefbUHliyGnGP1kC2sGBst4uMyfnzWTVQbh2l1HiMIvKSh0sGaRm6p8MS0kFG4Oq7KVj7Y9Ho1VXsiNYF2FUaaMjaJaCOUal1KXrenLx2yVPAJs19e5fnvPZU/Gxzy2QH7l73ceeuKZBwnAT/Z9fQwWML7HZUzGZD57Gr94w82LWJz7aW/AAlOKV2wAiFHYeMdMXKBcFV2yznop6/oknR4wc9L5MUjC4RIU/pU/QHOrfFTo+70z07UdhZeu5GBvvLSnRzaqfVUuMd+dipCNY/bQ9pTgpJP9iOU3hM54PllNWsjmiC9W8DiQIxgcsgz5pVAMKach+BHe4hiT4l1to/jVY7KRYbkK693TSd/lTmi8B1LKt6xB6EcOc92v10za5gBLt6Z5v8KR3gD/MSzAVLPhsvg1OMidKy3NLusHVjMQpsac227J0NI0+a58wAcFPDCTcGZXUr681SbkRZgRwtlkhHyWgo94uaN6+6ZtoAOPYhrqTm8b+k7vNTiUycU2ekvf6FtC2V8VDxEm6BknFMjVXxIgWk3AdXoGiKcBJIM8+zfOTPfPKlmdGCheLCGfXsnjN+fTpq2CxXX2UFAuQCsgdZY269EIStKc/nYdV9hKXNX2E0OPTa/qKHak6auKPa47BfOIR+jQCVgvDfucPsM+Sw/7xMp4QyjQw7+jSW8DqdV0VyOzSj+RGuwxzzBu7uZWA7/Ac07gKPMJSul/7CZ+b1FwhOTuCfK6Y3d538TYq8hMTJ1dzkQOqvNrfLHUTzcgTiEBtMjw/vlTM2Bv3DqQuVVpn7B+lfMZ+6ljlRjWs8eOd3DLu9IOfh559SBMM7fkC1oVbdrjRatXbK2C11lato+XaMPxdVM8cialhpSw44hS3nyeFWWeeYholC9GPns808L2kXsFa86rPZQAlKAkZKeLGC/4ZZCVl3/kyH8sECDGMhcAYZJFmI5Sdoc9J4DDhRKumi7ob03UNoXr6uS3bE/nvnk58uulNKaFso8sQi7mxUmaH47E302Jp0OG2y9uUEz7pLitN1rXBBUGjgBJYHYdy9a/CnAGSpbluUf8fmMD9aeEitXyzcCVoOJqUFnhBBWBwG/yv9KiXq8W1duT33HeusXnq3jmc3NfcpevWRznNEYJs5X8bkxJhrtqg7r7bVCvod3iNqS0uxZM93bRvwvQjML1mrauX3xrBxZyuHGqXsaeMzJqP1PRkSNrMPXYpKkltasVdR+c6u7oAko6YJ/2QG6vltoQxhlNqjIvpqomPJUtuwA9yZ7dbr8vr7An1S5qqj2pKXqoL32EvQdsrxugRxU3dEhL53+W4k73difpS34g5Yy82m863fteqqH9bDZo92JpiKV/FvJGqG6qVG8o11wbh3rG9s6pbr85qusp01uieou+3gvVrTdH9R6R9rNx2XprGtY3lrdE5RZ1vRMq68VjH69S3kHhxUOW3mtfjaGEZef5ivdtAjZogRIg3uPXa++Jffy1H92fW0Bgp9X/PZWN++rfx+y7/wE= -------------------------------------------------------------------------------- /quickstart/Makefile: -------------------------------------------------------------------------------- 1 | # Enable debugging and suppress pesky warnings 2 | CFLAGS ?= -g -w 3 | 4 | all: vulnerable 5 | 6 | clean: 7 | rm -f vulnerable 8 | 9 | vulnerable: vulnerable.c 10 | ${CC} ${CFLAGS} vulnerable.c -o vulnerable 11 | -------------------------------------------------------------------------------- /quickstart/README.md: -------------------------------------------------------------------------------- 1 | These instructions lead you through setup and fuzzing of a sample program. 2 | 3 | # Setup 4 | 5 | Jump to the appropriate part of this Setup section based on what you're configuring, then go to the next section 6 | (Building AFL). 7 | 8 | ## Logging in to the provided instance 9 | 10 | If you're reading these instructions then you've probably already made it! Skip to the vulnerable program section. 11 | 12 | ## Running the docker image locally 13 | 14 | See the "Running locally" section of docker/README.md, then skip to the vulnerable program section. 15 | 16 | ## Setting up your own machine manually 17 | 18 | Install dependencies (this is more than you need just for AFL, but you'll end up wanting these during the workshop; see 19 | the Dockerfile for a comprehensive list of dependencies for all of the challenges): 20 | 21 | $ sudo apt-get install git build-essential curl libssl-dev sudo libtool libtool-bin libglib2.0-dev bison flex automake python3 python3-dev python3-setuptools libpixman-1-dev gcc-9-plugin-dev cgroup-tools \ 22 | clang-11 clang-tools-11 libc++1-11 libc++-11-dev libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev libomp5-11 lld-11 lldb-11 python3-lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools 23 | 24 | Work around some Ubuntu annoyances 25 | 26 | $ sudo update-alternatives --install /usr/bin/clang clang `which clang-11.0` 1 27 | $ sudo update-alternatives --install /usr/bin/clang++ clang++ `which clang++-11.0` 1 28 | $ sudo update-alternatives --install /usr/bin/llvm-config llvm-config `which llvm-config-11.0` 1 29 | $ sudo update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer `which llvm-symbolizer-11.0` 1 30 | 31 | Get, build, and install afl: 32 | 33 | ```shell 34 | $ git clone https://github.com/AFLplusplus/AFLplusplus 35 | $ cd AFLplusplus 36 | $ git checkout 2.68c # if you want a specific version, otherwise skip this step 37 | $ make distrib 38 | $ sudo make install 39 | ``` 40 | 41 | Make system not interfere with crash detection, plus some other tweaks: 42 | 43 | $ ~/AFLplusplus/afl-system-config 44 | 45 | # The `vulnerable` program 46 | 47 | Build our quickstart program using the instrumented compiler: 48 | 49 | $ cd quickstart 50 | $ CC=afl-clang-fast AFL_HARDEN=1 make 51 | 52 | Test it: 53 | 54 | $ ./vulnerable 55 | # Press enter to get usage instructions. 56 | # Test it on one of the provided inputs: 57 | $ ./vulnerable < inputs/u 58 | 59 | # Fuzzing 60 | 61 | Fuzz it: 62 | 63 | $ afl-fuzz -i inputs -o out ./vulnerable 64 | 65 | Your session should soon resemble this: ![fuzzing session](./afl-screenshot.png) 66 | 67 | For comparison you could also test without the provided example inputs, e.g.: 68 | 69 | $ mkdir in 70 | $ echo "my seed" > in/a 71 | $ afl-fuzz -i in -o out ./vulnerable 72 | -------------------------------------------------------------------------------- /quickstart/afl-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykter/afl-training/628042ea77a1c0ada826618f77130e4b5d810231/quickstart/afl-screenshot.png -------------------------------------------------------------------------------- /quickstart/inputs/head: -------------------------------------------------------------------------------- 1 | head 20 This string is going to be truncated at the 20th position. 2 | -------------------------------------------------------------------------------- /quickstart/inputs/u: -------------------------------------------------------------------------------- 1 | u 4 capsme 2 | -------------------------------------------------------------------------------- /quickstart/vulnerable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define INPUTSIZE 100 7 | 8 | int process(char *input) 9 | { 10 | char *out; 11 | char *rest; 12 | int len; 13 | if (strncmp(input, "u ", 2) == 0) 14 | { // upper case command 15 | char *rest; 16 | len = strtol(input + 2, &rest, 10); // how many characters of the string to upper-case 17 | rest += 1; // skip the first char (should be a space) 18 | out = malloc(len + strlen(input)); // could be shorter, but play it safe 19 | if (len > (int)strlen(input)) 20 | { 21 | printf("Specified length %d was larger than the input!\n", len); 22 | return 1; 23 | } 24 | else if (out == NULL) 25 | { 26 | printf("Failed to allocate memory\n"); 27 | return 1; 28 | } 29 | for (int i = 0; i != len; i++) 30 | { 31 | char c = rest[i]; 32 | if (c > 96 && c < 123) // ascii a-z 33 | { 34 | c -= 32; 35 | } 36 | out[i] = c; 37 | } 38 | out[len] = 0; 39 | strcat(out, rest + len); // append the remaining text 40 | printf("%s", out); 41 | free(out); 42 | } 43 | else if (strncmp(input, "head ", 5) == 0) 44 | { // head command 45 | if (strlen(input) > 6) 46 | { 47 | len = strtol(input + 4, &rest, 10); 48 | rest += 1; // skip the first char (should be a space) 49 | rest[len] = '\0'; // truncate string at specified offset 50 | printf("%s\n", rest); 51 | } 52 | else 53 | { 54 | fprintf(stderr, "head input was too small\n"); 55 | } 56 | } 57 | else if (strcmp(input, "surprise!\n") == 0) 58 | { 59 | // easter egg! 60 | *(char *)1 = 2; 61 | } 62 | else 63 | { 64 | return 1; 65 | } 66 | return 0; 67 | } 68 | 69 | int main(int argc, char *argv[]) 70 | { 71 | char *usage = "Usage: %s\n" 72 | "Text utility - accepts commands and data on stdin and prints results to stdout.\n" 73 | "\tInput | Output\n" 74 | "\t------------------+-----------------------\n" 75 | "\tu | Uppercased version of the first bytes of .\n" 76 | "\thead | The first bytes of .\n"; 77 | char input[INPUTSIZE] = {0}; 78 | 79 | // Slurp input 80 | if (read(STDIN_FILENO, input, INPUTSIZE) < 0) 81 | { 82 | fprintf(stderr, "Couldn't read stdin.\n"); 83 | } 84 | 85 | int ret = process(input); 86 | if (ret) 87 | { 88 | fprintf(stderr, usage, argv[0]); 89 | }; 90 | return ret; 91 | } 92 | --------------------------------------------------------------------------------