From e8252b0f3292a12f5fd04cffde3d17480db12128 Mon Sep 17 00:00:00 2001 From: Lesbian Date: Wed, 3 Oct 2018 11:28:44 -0400 Subject: [PATCH] Initial clone --- .gitignore | 4 + LICENSE | 16 ++ Makefile | 6 + README | 10 ++ clownflare.c | 193 ++++++++++++++++++++++ test.sh | 2 + xml.c | 447 +++++++++++++++++++++++++++++++++++++++++++++++++++ xml.h | 45 ++++++ 8 files changed, 723 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 clownflare.c create mode 100755 test.sh create mode 100644 xml.c create mode 100644 xml.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a97b425 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +clownflare +*.o +*.core + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5b43ddc --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2018 Hiltjo Posthuma + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..26c44bd --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +build: + cc xml.c clownflare.c -o clownflare -Wall + +clean: + rm -f clownflare + diff --git a/README b/README new file mode 100644 index 0000000..feb01e9 --- /dev/null +++ b/README @@ -0,0 +1,10 @@ +ugly Cloudflare/clownflare JS challenge cracker + +Usage: + +pass the HTML source through this program: + +clownflare 'someurl' < htmlsource + +open the url in your browser. + diff --git a/clownflare.c b/clownflare.c new file mode 100644 index 0000000..7f2b7b8 --- /dev/null +++ b/clownflare.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#ifdef __OpenBSD__ +#include +#else +#define pledge(a,b) 0 +#endif + +#include "xml.h" + +static XMLParser x; +static char tag[256], inputname[256]; + +static char jschl_vc[256]; +static char jschl_answer[256]; +static char pass[256]; +static char script[4096]; + +void +xmltagstart(struct xmlparser *x, const char *t, size_t tl) +{ + if (!strcmp(t, "input") || !strcmp(t, "script")) + snprintf(tag, sizeof(tag), "%s", t); +} + +void +xmltagend(struct xmlparser *x, const char *t, size_t tl, int st) +{ + if (!strcmp(tag, "input")) + inputname[0] = tag[0] = '\0'; + else if (!strcmp(tag, "script")) + tag[0] = '\0'; +} + +void +xmlattr(struct xmlparser *x, const char *t, size_t tl, + const char *a, size_t al, const char *v, size_t vl) +{ + if (strcmp(tag, "input")) + return; + + if (!strcmp(a, "name")) { + if (!strcmp(v, "jschl_vc") || !strcmp(v, "pass")) + snprintf(inputname, sizeof(inputname), "%s", v); + } else if (inputname[0] && !strcmp(a, "value")) { + if (!strcmp(inputname, "jschl_vc")) + snprintf(jschl_vc, sizeof(jschl_vc), "%s", v); + else if (!strcmp(inputname, "pass")) + snprintf(pass, sizeof(pass), "%s", v); + } +} + +void +xmlcdata(struct xmlparser *x, const char *d, size_t dl) +{ + char tmp[sizeof(script)]; + + if (strcmp(tag, "script")) + return; + + snprintf(tmp, sizeof(tmp), "%s%s", script, d); + memcpy(script, tmp, sizeof(script)); +} + +int +calcvalue(char *script) +{ + char *stopbreaking = "s,t,o,p,b,r,e,a,k,i,n,g,f, "; + char var[32] = "", key[32] = "", varkey[64] = ""; + char *p, *s; + int op, pc, cv, nv, v = 0; + + if (!(s = strstr(script, stopbreaking))) + return v; + s += strlen(stopbreaking); + + for (p = s; *p; p++) { + if (*p == '=') { + *p = '\0'; + snprintf(var, sizeof(var), "%s", s); + *p = '='; + break; + } + } + if (!var[0]) + return v; + if (!(s = strchr(p, '"'))) + return v; + s++; + + for (p = s; *p; p++) { + if (*p == '"') { + *p = '\0'; + snprintf(key, sizeof(key), "%s", s); + *p = '"'; + break; + } + } + if (!key[0]) + return v; + + snprintf(varkey, sizeof(varkey), "%s.%s", var, key); + + if (!(s = strchr(p, ':'))) + return v; + s++; + + op = '+'; + while (1) { +// printf("op: %c\n", op); + + pc = cv = nv = 0; + for (s = p; *s; s++) { + if (*s == ';') { + nv += cv; +// printf("nv: %d\n", nv); + break; + } + switch (*s) { + case '(': + nv += cv; + cv = 0; + break; + case '!': + case '+': + if (pc == '!') + cv++; + break; + case ')': + if (pc == ')') + break; + +// printf(" cv: %d\n", cv); + nv *= 10; + + break; + } +// printf(" %c\n", *s); + pc = *s; + } + + switch (op) { + case '-': v -= nv; break; + case '+': v += nv; break; + case '*': v *= nv; break; + } + + if (!(s = strstr(p, varkey))) + return v; + p = s + strlen(varkey); + op = *p; + } + + return v; +} + +int +main(int argc, char *argv[]) +{ + int value; + + if (pledge("stdio", NULL) < 0) + err(1, "pledge"); + + if (argc != 2) { + fprintf(stderr, "%s \n", argv[0]); + return 1; + } + + x.xmlattr = xmlattr; + x.xmlcdata = xmlcdata; + x.xmltagstart = xmltagstart; + x.xmltagend = xmltagend; + x.getnext = getchar; + + xml_parse(&x); + + if (!(value = calcvalue(script))) + return 1; + value += strlen(argv[1]); + snprintf(jschl_answer, sizeof(jschl_answer), "%d", value); + + printf("jschl_vc = %s\n", jschl_vc); + printf("pass = %s\n", pass); + printf("jschl_answer = %s\n", jschl_answer); + printf("\n"); + printf("https://%s/cdn-cgi/l/chk_jschl?jschl_vc=%s&pass=%s&jschl_answer=%s\n", + argv[1], jschl_vc, pass, jschl_answer); + + return 0; +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..c4ff20a --- /dev/null +++ b/test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +curl -m 10 -H 'User-Agent:' 'https://torrentz2.eu/' | ./clownflare "torrentz2.eu" diff --git a/xml.c b/xml.c new file mode 100644 index 0000000..53240b2 --- /dev/null +++ b/xml.c @@ -0,0 +1,447 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +static void +xml_parseattrs(XMLParser *x) +{ + size_t namelen = 0, valuelen; + int c, endsep, endname = 0; + + while ((c = x->getnext()) != EOF) { + if (isspace(c)) { /* TODO: simplify endname ? */ + if (namelen) + endname = 1; + continue; + } + if (c == '?') + ; /* ignore */ + else if (c == '=') { + x->name[namelen] = '\0'; + } else if (namelen && ((endname && isalpha(c)) || (c == '>' || c == '/'))) { + /* attribute without value */ + x->name[namelen] = '\0'; + if (x->xmlattrstart) + x->xmlattrstart(x, x->tag, x->taglen, x->name, namelen); + if (x->xmlattr) + x->xmlattr(x, x->tag, x->taglen, x->name, namelen, "", 0); + if (x->xmlattrend) + x->xmlattrend(x, x->tag, x->taglen, x->name, namelen); + endname = 0; + x->name[0] = c; + namelen = 1; + } else if (namelen && (c == '\'' || c == '"')) { + /* attribute with value */ + endsep = c; /* c is end separator */ + if (x->xmlattrstart) + x->xmlattrstart(x, x->tag, x->taglen, x->name, namelen); + for (valuelen = 0; (c = x->getnext()) != EOF;) { + if (c == '&') { /* entities */ + x->data[valuelen] = '\0'; + /* call data function with data before entity if there is data */ + if (valuelen && x->xmlattr) + x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen); + x->data[0] = c; + valuelen = 1; + while ((c = x->getnext()) != EOF) { + if (c == endsep) + break; + if (valuelen < sizeof(x->data) - 1) + x->data[valuelen++] = c; + else { + /* TODO: entity too long? this should be very strange. */ + x->data[valuelen] = '\0'; + if (x->xmlattr) + x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen); + valuelen = 0; + break; + } + if (c == ';') { + x->data[valuelen] = '\0'; + if (x->xmlattrentity) + x->xmlattrentity(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen); + valuelen = 0; + break; + } + } + } else if (c != endsep) { + if (valuelen < sizeof(x->data) - 1) { + x->data[valuelen++] = c; + } else { + x->data[valuelen] = '\0'; + if (x->xmlattr) + x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen); + x->data[0] = c; + valuelen = 1; + } + } + if (c == endsep) { + x->data[valuelen] = '\0'; + if (x->xmlattr) + x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen); + if (x->xmlattrend) + x->xmlattrend(x, x->tag, x->taglen, x->name, namelen); + break; + } + } + namelen = endname = 0; + } else if (namelen < sizeof(x->name) - 1) { + x->name[namelen++] = c; + } + if (c == '>') { + break; + } else if (c == '/') { + x->isshorttag = 1; + namelen = 0; + x->name[0] = '\0'; + } + } +} + +static void +xml_parsecomment(XMLParser *x) +{ + size_t datalen = 0, i = 0; + int c; + + if (x->xmlcommentstart) + x->xmlcommentstart(x); + while ((c = x->getnext()) != EOF) { + if (c == '-' || c == '>') { + if (x->xmlcomment) { + x->data[datalen] = '\0'; + x->xmlcomment(x, x->data, datalen); + datalen = 0; + } + } + + if (c == '-') { + if (++i > 2) { + if (x->xmlcomment) + for (; i > 2; i--) + x->xmlcomment(x, "-", 1); + i = 2; + } + continue; + } else if (c == '>' && i == 2) { + if (x->xmlcommentend) + x->xmlcommentend(x); + return; + } else if (i) { + if (x->xmlcomment) { + for (; i > 0; i--) + x->xmlcomment(x, "-", 1); + } + i = 0; + } + + if (datalen < sizeof(x->data) - 1) { + x->data[datalen++] = c; + } else { + x->data[datalen] = '\0'; + if (x->xmlcomment) + x->xmlcomment(x, x->data, datalen); + x->data[0] = c; + datalen = 1; + } + } +} + +static void +xml_parsecdata(XMLParser *x) +{ + size_t datalen = 0, i = 0; + int c; + + if (x->xmlcdatastart) + x->xmlcdatastart(x); + while ((c = x->getnext()) != EOF) { + if (c == ']' || c == '>') { + if (x->xmlcdata) { + x->data[datalen] = '\0'; + x->xmlcdata(x, x->data, datalen); + datalen = 0; + } + } + + if (c == ']') { + if (++i > 2) { + if (x->xmlcdata) + for (; i > 2; i--) + x->xmlcdata(x, "]", 1); + i = 2; + } + continue; + } else if (c == '>' && i == 2) { + if (x->xmlcdataend) + x->xmlcdataend(x); + return; + } else if (i) { + if (x->xmlcdata) + for (; i > 0; i--) + x->xmlcdata(x, "]", 1); + i = 0; + } + + if (datalen < sizeof(x->data) - 1) { + x->data[datalen++] = c; + } else { + x->data[datalen] = '\0'; + if (x->xmlcdata) + x->xmlcdata(x, x->data, datalen); + x->data[0] = c; + datalen = 1; + } + } +} + +int +xml_codepointtoutf8(uint32_t cp, uint32_t *utf) +{ + if (cp >= 0x10000) { + /* 4 bytes */ + *utf = 0xf0808080 | ((cp & 0xfc0000) << 6) | + ((cp & 0x3f000) << 4) | ((cp & 0xfc0) << 2) | + (cp & 0x3f); + return 4; + } else if (cp >= 0x00800) { + /* 3 bytes */ + *utf = 0xe08080 | + ((cp & 0x3f000) << 4) | ((cp & 0xfc0) << 2) | + (cp & 0x3f); + return 3; + } else if (cp >= 0x80) { + /* 2 bytes */ + *utf = 0xc080 | + ((cp & 0xfc0) << 2) | (cp & 0x3f); + return 2; + } + *utf = cp & 0xff; + return *utf ? 1 : 0; /* 1 byte */ +} + +ssize_t +xml_namedentitytostr(const char *e, char *buf, size_t bufsiz) +{ + static const struct { + char *entity; + int c; + } entities[] = { + { .entity = "&", .c = '&' }, + { .entity = "<", .c = '<' }, + { .entity = ">", .c = '>' }, + { .entity = "'", .c = '\'' }, + { .entity = """, .c = '"' }, + { .entity = "&", .c = '&' }, + { .entity = "<", .c = '<' }, + { .entity = ">", .c = '>' }, + { .entity = "&APOS;", .c = '\'' }, + { .entity = """, .c = '"' } + }; + size_t i; + + /* buffer is too small */ + if (bufsiz < 2) + return -1; + + /* doesn't start with &: can't match */ + if (*e != '&') + return 0; + + for (i = 0; i < sizeof(entities) / sizeof(*entities); i++) { + if (!strcmp(e, entities[i].entity)) { + buf[0] = entities[i].c; + buf[1] = '\0'; + return 1; + } + } + return 0; +} + +ssize_t +xml_numericentitytostr(const char *e, char *buf, size_t bufsiz) +{ + uint32_t l = 0, cp = 0; + size_t b, len; + char *end; + + /* buffer is too small */ + if (bufsiz < 5) + return -1; + + /* not a numeric entity */ + if (e[0] != '&' || e[1] != '#') + return 0; + + /* e[1] == '#', numeric / hexadecimal entity */ + e += 2; /* skip "&#" */ + errno = 0; + /* hex (16) or decimal (10) */ + if (*e == 'x') + l = strtoul(e + 1, &end, 16); + else + l = strtoul(e, &end, 10); + /* invalid value or not a well-formed entity */ + if (errno || *end != ';') + return 0; + len = xml_codepointtoutf8(l, &cp); + /* make string */ + for (b = 0; b < len; b++) + buf[b] = (cp >> (8 * (len - 1 - b))) & 0xff; + buf[len] = '\0'; + + return (ssize_t)len; +} + +/* convert named- or numeric entity string to buffer string + * returns byte-length of string. */ +ssize_t +xml_entitytostr(const char *e, char *buf, size_t bufsiz) +{ + /* buffer is too small */ + if (bufsiz < 5) + return -1; + /* doesn't start with & */ + if (e[0] != '&') + return 0; + /* named entity */ + if (e[1] != '#') + return xml_namedentitytostr(e, buf, bufsiz); + else /* numeric entity */ + return xml_numericentitytostr(e, buf, bufsiz); +} + +void +xml_parse(XMLParser *x) +{ + int c, ispi; + size_t datalen, tagdatalen, taglen; + + if (!x->getnext) + return; + while ((c = x->getnext()) != EOF && c != '<') + ; /* skip until < */ + + while (c != EOF) { + if (c == '<') { /* parse tag */ + if ((c = x->getnext()) == EOF) + return; + x->tag[0] = '\0'; + x->taglen = 0; + if (c == '!') { /* cdata and comments */ + for (tagdatalen = 0; (c = x->getnext()) != EOF;) { + if (tagdatalen <= sizeof("[CDATA[") - 1) /* if (d < sizeof(x->data)) */ + x->data[tagdatalen++] = c; /* TODO: prevent overflow */ + if (c == '>') + break; + else if (c == '-' && tagdatalen == sizeof("--") - 1 && + (x->data[0] == '-')) { + xml_parsecomment(x); + break; + } else if (c == '[') { + if (tagdatalen == sizeof("[CDATA[") - 1 && + !strncmp(x->data, "[CDATA[", tagdatalen)) { + xml_parsecdata(x); + break; + } + } + } + } else { + /* normal tag (open, short open, close), processing instruction. */ + if (isspace(c)) + while ((c = x->getnext()) != EOF && isspace(c)) + ; + if (c == EOF) + return; + x->tag[0] = c; + ispi = (c == '?') ? 1 : 0; + x->isshorttag = ispi; + taglen = 1; + while ((c = x->getnext()) != EOF) { + if (c == '/') /* TODO: simplify short tag? */ + x->isshorttag = 1; /* short tag */ + else if (c == '>' || isspace(c)) { + x->tag[taglen] = '\0'; + if (x->tag[0] == '/') { /* end tag, starts with taglen = --taglen; /* len -1 because of / */ + if (taglen && x->xmltagend) + x->xmltagend(x, &(x->tag)[1], x->taglen, 0); + } else { + x->taglen = taglen; + /* start tag */ + if (x->xmltagstart) + x->xmltagstart(x, x->tag, x->taglen); + if (isspace(c)) + xml_parseattrs(x); + if (x->xmltagstartparsed) + x->xmltagstartparsed(x, x->tag, x->taglen, x->isshorttag); + } + /* call tagend for shortform or processing instruction */ + if ((x->isshorttag || ispi) && x->xmltagend) + x->xmltagend(x, x->tag, x->taglen, 1); + break; + } else if (taglen < sizeof(x->tag) - 1) + x->tag[taglen++] = c; + } + } + } else { + /* parse tag data */ + datalen = 0; + if (x->xmldatastart) + x->xmldatastart(x); + while ((c = x->getnext()) != EOF) { + if (c == '&') { + if (datalen) { + x->data[datalen] = '\0'; + if (x->xmldata) + x->xmldata(x, x->data, datalen); + } + x->data[0] = c; + datalen = 1; + while ((c = x->getnext()) != EOF) { + if (c == '<') + break; + if (datalen < sizeof(x->data) - 1) + x->data[datalen++] = c; + if (isspace(c)) + break; + else if (c == ';') { + x->data[datalen] = '\0'; + if (x->xmldataentity) + x->xmldataentity(x, x->data, datalen); + datalen = 0; + break; + } + } + } else if (c != '<') { + if (datalen < sizeof(x->data) - 1) { + x->data[datalen++] = c; + } else { + x->data[datalen] = '\0'; + if (x->xmldata) + x->xmldata(x, x->data, datalen); + x->data[0] = c; + datalen = 1; + } + } + if (c == '<') { + x->data[datalen] = '\0'; + if (x->xmldata && datalen) + x->xmldata(x, x->data, datalen); + if (x->xmldataend) + x->xmldataend(x); + break; + } + } + } + } +} + diff --git a/xml.h b/xml.h new file mode 100644 index 0000000..de24bbe --- /dev/null +++ b/xml.h @@ -0,0 +1,45 @@ +typedef struct xmlparser { + /* handlers */ + void (*xmlattr)(struct xmlparser *, const char *, size_t, + const char *, size_t, const char *, size_t); + void (*xmlattrend)(struct xmlparser *, const char *, size_t, + const char *, size_t); + void (*xmlattrstart)(struct xmlparser *, const char *, size_t, + const char *, size_t); + void (*xmlattrentity)(struct xmlparser *, const char *, size_t, + const char *, size_t, const char *, size_t); + void (*xmlcdatastart)(struct xmlparser *); + void (*xmlcdata)(struct xmlparser *, const char *, size_t); + void (*xmlcdataend)(struct xmlparser *); + void (*xmlcommentstart)(struct xmlparser *); + void (*xmlcomment)(struct xmlparser *, const char *, size_t); + void (*xmlcommentend)(struct xmlparser *); + void (*xmldata)(struct xmlparser *, const char *, size_t); + void (*xmldataend)(struct xmlparser *); + void (*xmldataentity)(struct xmlparser *, const char *, size_t); + void (*xmldatastart)(struct xmlparser *); + void (*xmltagend)(struct xmlparser *, const char *, size_t, int); + void (*xmltagstart)(struct xmlparser *, const char *, size_t); + void (*xmltagstartparsed)(struct xmlparser *, const char *, + size_t, int); + + int (*getnext)(void); + + /* current tag */ + char tag[1024]; + size_t taglen; + /* current tag is in short form ? */ + int isshorttag; + /* current attribute name */ + char name[256]; + /* data buffer used for tag data, cdata and attribute data */ + char data[BUFSIZ]; +} XMLParser; + +int xml_codepointtoutf8(uint32_t, uint32_t *); +ssize_t xml_entitytostr(const char *, char *, size_t); +ssize_t xml_namedentitytostr(const char *, char *, size_t); +ssize_t xml_numericentitytostr(const char *, char *, size_t); + +void xml_parse(XMLParser *); +