
KnightCTF 2023
Nguồn ở đây nhá các bạn: 💀👆👆👆💀
Xin chào năm mới mọi người, Team tôi khai xuân năm mới với một giải nhẹ nhàng. Một giải server máy chủ gặp trục trặc nhiều quá…. Nhưng không sao nhiều bài cũng ổn đối với mình, nó cũng nhiều bài làm đa dạng sau một thời gian tôi ngủ đông. Đơn giản thui thui bắt đầu luôn nhé…👻
1: KrackMe 1.0 (Solved)
Vâng bài đầu tiên nhé. Như thường lệ mình sẽ kiểm tra kiểu file bằng HxD
, CFF Explorer
… để check cái thứ cần thiết nhé và sau đó chạy IDA pro
và phần mình quan tâm đây là:
Một số kiến thức phổ cập trước khi vào bài nhá:
-
Arguments to main:(link)
int main( void ) int main( int argc, char *argv[] ) int main( int argc, char *argv[], char *envp[] )
- Hàm
main
cũng như các hàm khác có nghĩa là đều nhận đối số để xử lí. Tuy nhiên tham số trong hàmmain
đã dc C định nghĩa sẵn, và có thứ tự:int agrc
: đối số cho biết tham số đã nhập, kể cả tên chương trình.char *argv[]
hoặcchar **argv
: mảng cácpointer
trỏ đến các chuỗi là tham số đi theo sau tên chương trình khi chạy chương trình từ DOS. Chuỗi là tên chương trình luôn được chỉ bởiargv[0]
.char *envp[]
, is an array of pointers to environment variables (là một mảng các trỏ tới các môi trường). Mảng này được kết thúc bởi một con trỏnull
(a null pointer).
-
Nếu một chương trình được gọi nà không có đối số dòng lệnh sẽ nhận giá trị 1 cho
argc
, vì tên tệp của tệp thực thi được đặt trongargv[0]
. Các chuỗi được trỏ bởiargv[1]
đếnargv[argc-1]
đại diện tham số chương trình. -
Các tham số
argc
vàargv
có thể sửa đổi được và giữ lại các giá trị được lưu trữ lần cuối giữa khởi động chương trình và kết thúc chương trình. -
HIBYTE()
func - là hàm để lưu trữ byte bậc cao (high-order byte).// phần định nghĩa HIBYTE trong C #define HIBYTE(w) ((char)(((__int16)(w) >> 8) & 0xFF))
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int i; // [rsp+10h] [rbp-170h]
unsigned int j; // [rsp+10h] [rbp-170h]
unsigned int k; // [rsp+10h] [rbp-170h]
unsigned int m; // [rsp+10h] [rbp-170h]
unsigned int n; // [rsp+10h] [rbp-170h]
int ii; // [rsp+10h] [rbp-170h]
int jj; // [rsp+10h] [rbp-170h]
unsigned int kk; // [rsp+10h] [rbp-170h]
int v12; // [rsp+14h] [rbp-16Ch]
__int16 v13[10]; // [rsp+18h] [rbp-168h] BYREF
__int16 v14[10]; // [rsp+2Ch] [rbp-154h] BYREF
char v15[32]; // [rsp+40h] [rbp-140h] BYREF
char v16[32]; // [rsp+60h] [rbp-120h] BYREF
char v17[48]; // [rsp+80h] [rbp-100h] BYREF
char v18[48]; // [rsp+B0h] [rbp-D0h] BYREF
char s[64]; // [rsp+E0h] [rbp-A0h] BYREF
char v20[72]; // [rsp+120h] [rbp-60h] BYREF
unsigned __int64 v21; // [rsp+168h] [rbp-18h]
v21 = __readfsqword(0x28u);
init(argc, argv, envp);
strcpy(v17, "You don't have access to KrackMe 1.0 !");
strcpy(v18, "Since you are here let me ask you something...");
strcpy(v15, "Please enter the flag : ");
strcpy(v16, "Oh My God ! What is that ?");
strcpy(v20, "Did you know, Bangladesh has the longest natural beach?...");
if ( argc != 5 ) //kiểm tra số lượng tham số đầu vào là 5
{
for ( i = 0; i <= 0x26; ++i )
{
putchar(v17[i]);
fflush(_bss_start);
usleep(0x186A0u);
}
putchar(10);
for ( j = 0; j <= 0x2E; ++j )
{
putchar(v18[j]);
fflush(_bss_start);
usleep(0x186A0u);
}
putchar(10);
for ( k = 0; k <= 0x3A; ++k )
{
putchar(v20[k]);
fflush(_bss_start);
usleep(0x186A0u);
}
putchar(10);
exit(0);
}
for ( m = 0; m <= 0x18; ++m )
{
putchar(v15[m]);
fflush(_bss_start);
usleep(0x186A0u);
}
fgets(s, 50, stdin);
if ( strlen(s) != 40 ) // kiểm ra kích thước của chuỗi là 40
{
for ( n = 0; n <= 0x1A; ++n )
{
putchar(v16[n]);
fflush(_bss_start);
usleep(0x186A0u);
}
putchar(10);
exit(0);
}
strcpy((char *)v13, "mer`]MtGe");
strcpy((char *)&v13[5], "aUG9UeDoU");
strcpy((char *)v14, "(G~Ty_G{(");
strcpy((char *)&v14[5], "v}QlOto|s");
v12 = 0;
for ( ii = 0; ii < strlen((const char *)v13); ++ii ) //kiểm tra của chuỗi đúng hay sai nếu sai 1 trong các điều kiện sẽ out program.
{
if ( *((_BYTE *)v13 + ii) != ((unsigned __int8)(v20[14] ^ v16[8]) ^ (unsigned __int8)s[ii]) )
{
v12 = 0;
break;
}
if ( *((_BYTE *)&v14[5] + ii) != ((unsigned __int8)(v17[11] ^ v17[1]) ^ (unsigned __int8)s[ii + 27]) )
{
v12 = 0;
break;
}
if ( *((_BYTE *)&v13[5] + ii) != ((unsigned __int8)(v17[1] ^ HIBYTE(v13[0])) ^ (unsigned __int8)s[ii + 9]) )
{
v12 = 0;
break;
}
if ( *((_BYTE *)v14 + ii) != ((unsigned __int8)(HIBYTE(v14[5]) ^ HIBYTE(v13[0])) ^ (unsigned __int8)s[ii + 18]) ) //func HIBYTE()
{
v12 = 0;
break;
}
v12 = 1;
}
if ( v12 == 1 ) //check in ra flag
{
puts("Congratulations !! ");
printf("Flag : ");
for ( jj = 0; jj <= 35; ++jj )
{
putchar(s[jj]);
fflush(_bss_start);
usleep(0x186A0u);
}
putchar(10);
}
else
{
for ( kk = 0; kk <= 0x1A; ++kk )
{
putchar(v16[kk]);
fflush(_bss_start);
usleep(0x186A0u);
}
}
return 0;
}
Như đã phân tích trên thì sẽ cũng ra được bài rồi. Và dưới đây là code python của tôi:
v13 = "mer`]MtGe "
v13a = "aUG9UeDoU "
v14 = "(G~Ty_G{( "
v14a = "v}QlOto|s "
v17 = "You don't have access to KrackMe 1.0 !"
v18 = "Since you are here let me ask you something..."
v16 = "Oh My God ! What is that ?"
v20 = "Did you know, Bangladesh has the longest natural beach?..."
for i in range(0,9):
for j in range(0,127):
if ord(v13[i]) == ord(v20[14]) ^ ord(v16[8])^j:
print(chr(j), end='')
print('\n')
# KCTF{kRaC♠
for i in range(0,10):
for j in range(0,127):
if ord(v14a[i]) == ord(v17[11]) ^ ord(v17[1])^j:
print(chr(j), end='')
print('\n')
# xs_bAzar}.
for i in range(0,10):
for j in range(0,127):
if ord(v13a[i]) == ord(v17[1]) ^ ord(v13[1])^j: # nó lấy giá trị HIBYTE của v13[0] 16bit là 'me' mà trong assembly -> HIBYTE sẽ nhận là 'e'
print(chr(j), end='')
print('\n')
# k_M3_oNe_*
for i in range(0,10):
for j in range(0,127):
if ord(v14[i]) == ord(v14a[1]) ^ ord(v13[1])^j:
print(chr(j), end='')
print('\n')
# 0_fLaG_c08
Vậy ta sắp xếp lại theo thứ tự thì ta được flag: KCTF{kRaCk_M3_oNe_0_fLaG_c0xs_bAzar}
.
2: The Activator (Solved)
Code hàm main:
Phần trên là phần show màn hình và cin
cái mà mình cần nhập. Tiếp đến là phần kiểm tra cái mà mình nhập:
Nó sẽ kiểm ra thông qua các func con khác để tạo thành 1 keygen hoản chỉnh và nếu không thỏa mãn 1 trong các điều kiện đó thì sẽ được nhảy đến phần này luôn:
Với bài này thì ta chỉ cần đọc lần lượt các hàm yêu cầu rồi viết lại các điều kiện đó và tạo keygen thôi. Dưới là tôi viết chương trình py, khi chạy thì chương trình sẽ cho bạn 1 keygen random.
from random import choice
p1 = []
p2 =[]
p3 = 'KOS'
p4 = []
p5 = []
# part 1 three number
for i in range(0,10):
for j in range(0,10):
for k in range(0,10):
temp = int(str(i) + str(j) + str(k))
if temp > 0 and temp < 366 and temp !=333:
p1.append(str(i) + str(j) + str(k))
# part 2 two number continue
for i in range(0,10):
for j in range(0,10):
temp = int(str(i) +str(j))
if temp ==95 or temp==96 or temp ==97 or temp==98 or temp ==99 or temp <=2:
p2.append(str(i) +str(j))
#part 4
for i in range(0,10):
for j in range(0,10):
for k in range(0,10):
for l in range(0,10):
for m in range(0,10):
for n in range(0,10):
for o in range(0,10):
temp = i +j+k+m+n+l+o
if temp % 7 ==0:
p4.append(str(i) +str(j)+str(k) +str(l)+str(m) +str(n)+str(o))
#part 5
for i in range(0,10):
for j in range(0,10):
for k in range(0,10):
for l in range(0,10):
for m in range(0,10):
if i <=9 and j <=9 and k <=9 and l <=9 and m <=9:
p5.append(str(i) +str(j)+str(k) +str(l)+str(m))
keygen = choice(p1) + choice(p2) + '-' + p3 + '-'+ choice(p4) + '-' + choice(p5)
print(keygen)
# KCTF{Th47_License_ch3cker_w4S_similar_t0_Wind0ws_95_OSR_Activator_Right?}
Lưu ý chương trình tôi viết nó hàn lâm quá vì chỉ các vòng lặp đơn giản, có cách khác là ta sự dụng các hàm sẵn của python thì sẽ thấy code ngăn hơn.
3: The Defuser (Solved)
Với bài này thì ta cần điền nhanh trước khi bom nổ:
- Hàm
signal()
là gì?- Trong thư viện C thì hàm
void (*signal(int sig, void (*func)(int)))(int)
để xử lý tín hiệu (handle signal), tức là một trình xử lý tín hiệu với số tín hiệusig
. sig
- Đây là số tín hiệu mà chức năng xử lý được đặt. Và sau đây là một số tín hiệu tiêu biểu quan trọng:SIGABRT
- (Signal Abort) - Chấm dứt bất thường, chẳng hạn như được khai báo bởi func.SIGFPE
- (Signal Floating-Point Exception) - phép toán số học sai, chẳng hạn như phép chia bằng 0 hoặc pháp toán dẫn đến tràn số (không nhất thiết phải có phép toán dấu phẩy động).SIGILL
- (Signal Illegal Instruction) - invalid function image, chẳng hạn như câu lệnh không hợp lệ. Điều này thường do code bị hỏng hoặc cố gáng thực thi dữ liệu.SIGINT
- (Signal Interrupt) - tín hiệu chú ý tương tác. Thường được tạo bởi người dùng ứng dụng.SIGSEGV
- (Signal Segmentation Violation) - truy cập không hợp lệ vào bộ lưu trữ. Khi một chương trình cố gắng đọc hoặc ghi bên ngoài bộ nhớ, nó được cấp phát cho nó.SIGTERM
- (Signal Terminate) - yêu cầu chấm dứt gửi đến chương trình.
- Trong thư viện C thì hàm
- Hàm
alarm()
sẽ khiến hệ thống tạo tín hiệuSIGALRM
cho quy trình sau khi hết số giây thời gian thực được chỉ định bằng giây.
Như vậy sau đó chương trình sẽ yêu cầu ta nhập gì đó để kiểu cầu xin họ cho bom không nổ🥲. và sau đó xử lý nó ở hàm sub_2760()
:
Rồi tôi debug nó xem sau: tất nhiên khi debug đến alarm(1)
thì chương trình sẽ dừng về nó sẽ đưa tín cho trình xử lý để dừng chương trình. Do đó khi debug qua nó thì tôi sửa 1 thành 1 thời gian bao la…; rồi nhập -> đi vào hàm sub_2760()
-> đến câu lệnh so sánh sửa cờ để chỉ hướng vào trong hàm sub_2596()
(hoặc sửa giá trị a1
như trên) -> rồi ta thấy chương trình sẽ giải mã và in ra cho ta flag….:
4: Help Jimmy (Solved)
Bài này là một game chọn đường đi cho Jimmy xem cho anh ấy đi lên rừng hay xuống biển. Ấy vậy khi chạy chọn 1 hay 2 đều chết))).
Code của main:
Một đoạn không hiểu thế làm thế nào để chọn đường cho đúng. Nên lúc đầu tôi tin vào F5
của IDA thế nên tôi bỏ qua nó sang task khác thì xong nhận được trợ giúp từ đồng đội thì tôi xem asm language của nó thì nhận ra trước khi nhập 1 hoặc 2 thì có so sánh và tôi cũng có thắc mắc tại sao lại gán 2 giá trị v23[1]
và v23[2]
cho 5 rồi để không. Thì ra nó só sánh 2 cái đó nếu bằng nhau thì ra đến phần luôn sai thế nên debug tôi sẽ sửa và tiếp tục xem sa0:
và rồi khi đến phần be bé đó ta đến với hàm giải mã flag và ra luôn flag hí hì:
5: Fan
Với thử thách này thì bài cho ta 1 file chưa code viết bằng python đã dùng dis()
để thành các bytecode. Nhiệm vụ của chúng ta là dịch lại thành code nguyên bản để đọc chương trình ra flag.
Và sau đây tôi sẽ cùng các bạn phân tích lần lượt như sau nhé:
# Method Name: <module>
# Filename: chall.py
# Argument count: 0
# Keyword-only arguments: 0
# Number of locals: 0
# Stack size: 5
# Flags: 0x00000040 (NOFREE)
# First Line: 1
1: 0 LOAD_CONST (<code object define_false at 0x7fbea9c650c0, file "chall.py", line 1>)
2 LOAD_CONST ('define_false')
4 MAKE_FUNCTION (Neither defaults, keyword-only args, annotations, nor closures)
6 STORE_NAME (define_false)
19: 8 LOAD_CONST (<code object define_true at 0x7fbea9c65540, file "chall.py", line 19>)
10 LOAD_CONST ('define_true')
12 MAKE_FUNCTION (Neither defaults, keyword-only args, annotations, nor closures)
14 STORE_NAME (define_true)
25: 16 LOAD_CONST (<code object define_both at 0x7fbea9699540, file "chall.py", line 25>)
18 LOAD_CONST ('define_both')
20 MAKE_FUNCTION (Neither defaults, keyword-only args, annotations, nor closures)
22 STORE_NAME (define_both)
40: 24 LOAD_NAME (__name__)
26 LOAD_CONST ('__main__')
28 COMPARE_OP (==)
30 POP_JUMP_IF_FALSE (to 66) # nhảy đến vị trí số 66 nếu điều kiện không thỏa mãn
41: 32 LOAD_CONST ('chr(75)chr(67)chr(84)chr(70)chr(123)')
34 LOAD_CONST ('chr(115)chr(105)chr(85)chr(85)chr(85)')
36 LOAD_CONST ('chr(109)chr(69)chr(51)chr(115)chr(115)chr(105)')
38 LOAD_CONST ('chr(105)chr(115)')
40 LOAD_CONST ('chr(103)chr(48)chr(79)chr(97)chr(116)chr(125)')
42 BUILD_LIST 5
44 STORE_NAME (s)
42: 46 LOAD_NAME (print)
48 LOAD_NAME (define_false)
50 LOAD_NAME (define_true)
52 LOAD_NAME (define_both)
54 LOAD_NAME (s)
56 CALL_FUNCTION (1 positional argument)
58 CALL_FUNCTION (1 positional argument)
60 CALL_FUNCTION (1 positional argument)
62 CALL_FUNCTION (1 positional argument)
64 POP_TOP
>> 66 LOAD_CONST (None)
68 RETURN_VALUE
Câu lệnh được sử dụng để disassemble python là python -m dis name_file.py
hoặc là dis.dis(obj)
- lưu ý nhớ import dis
. Một lưu ý nữa là khi LOAD_CONST
sẽ được tải ngược lại, giống như stack vào trước ra sau.
1, 19, 25, 40 là các vị trí đặt các hàm của chương trình. đầu tiên sẽ là định nghĩa hàm define_false()
, define_true
, define_both
, __main__
.
Một số câu lệnh cần biết:
STORE_NAME
- là để biểu thị tên biến được tạo gán, hoặc lưu trữ. Thường đi kèm với bytecodeLOAD_CONST
thực hiện việc gán dl cho biếnCOMPARE_OP
- toán tử so sánhPOP_JUMP_IF_FALSE
- nhảy tuyệt đối nếu điều kiện saiBUILD_LIST
- tạo listPOP_TOP
- removes the top-of-stack (TOS) item.RETURN_VALUE
- returns with TOS to the caller of the function.
Và sau đây tôi sẽ viết lại code:
def define_false(arg):
pass
def define_true(arg):
pass
def define_both(arg):
pass
if __name__ == '__main__':
s = [
'chr(103)chr(48)chr(79)chr(97)chr(116)chr(125)',
'chr(105)chr(115)',
'chr(109)chr(69)chr(51)chr(115)chr(115)chr(105)',
'chr(115)chr(105)chr(85)chr(85)chr(85)',
'chr(75)chr(67)chr(84)chr(70)chr(123)'
]# list gồm 5 phần tử
s = s[::-1]
print(define_false(define_true(define_both(s))))
Tiếp theo nhé:
# Method Name: define_false
# Filename: chall.py
# Argument count: 1
# Keyword-only arguments: 0
# Number of locals: 7
# Stack size: 4
# Flags: 0x00000043 (NOFREE | NEWLOCALS | OPTIMIZED)
# First Line: 1
2: 0 BUILD_LIST 0
2 STORE_FAST (lstr)
3: 4 LOAD_CONST (0)
6 STORE_FAST (u)
4: 8 LOAD_CONST ('')
10 STORE_FAST (packed)
5: 12 SETUP_LOOP (to 126)
14 LOAD_FAST (s)
16 GET_ITER
>> 18 FOR_ITER (to 124)
20 STORE_FAST (c)
6: 22 LOAD_FAST (c)
24 LOAD_CONST ('[')
26 COMPARE_OP (==)
28 POP_JUMP_IF_FALSE (to 54)
7: 30 LOAD_FAST (lstr)
32 LOAD_ATTR (append)
34 LOAD_FAST (packed)
36 LOAD_FAST (u)
38 BUILD_TUPLE 2
40 CALL_FUNCTION (1 positional argument)
42 POP_TOP
8: 44 LOAD_CONST ('')
46 STORE_FAST (packed)
9: 48 LOAD_CONST (0)
50 STORE_FAST (u)
52 JUMP_ABSOLUTE (to 18)
10: >> 54 LOAD_FAST (c)
56 LOAD_CONST (']')
58 COMPARE_OP (==)
60 POP_JUMP_IF_FALSE (to 88)
11: 62 LOAD_FAST (lstr)
64 LOAD_ATTR (pop)
66 CALL_FUNCTION (0 positional arguments)
68 UNPACK_SEQUENCE 2
70 STORE_FAST (prev_string)
72 STORE_FAST (num)
12: 74 LOAD_FAST (prev_string)
76 LOAD_FAST (num)
78 LOAD_FAST (packed)
80 BINARY_MULTIPLY
82 BINARY_ADD
84 STORE_FAST (packed)
86 JUMP_ABSOLUTE (to 18)
13: >> 88 LOAD_FAST (c)
90 LOAD_ATTR (isdigit)
92 CALL_FUNCTION (0 positional arguments)
94 POP_JUMP_IF_FALSE (to 114)
14: 96 LOAD_FAST (u)
98 LOAD_CONST (10)
100 BINARY_MULTIPLY
102 LOAD_GLOBAL (int)
104 LOAD_FAST (c)
106 CALL_FUNCTION (1 positional argument)
108 BINARY_ADD
110 STORE_FAST (u)
112 JUMP_ABSOLUTE (to 18)
16: >> 114 LOAD_FAST (packed)
116 LOAD_FAST (c)
118 INPLACE_ADD
120 STORE_FAST (packed)
122 JUMP_ABSOLUTE (to 18)
>> 124 POP_BLOCK
17: >> 126 LOAD_FAST (packed)
128 RETURN_VALUE
LOAD_GLOBAL
- load biến toàn cụcBUILD_TUPLE
- tạo typle (số lượng)JUMP_ABSOLUTE
- nhảy tuyệt đốiUNPACK_SEQUENCE
unpack TOS into count các giá trị riêng lẻ, được put onto the stack right-to-leftBINARY_MULTIPLY
- biểu thức nhânBINARY_ADD
- cộng
def define_false(s):
lstr = []
u = 0
packed = ''
for c in s:
if c == '[':
lstr.append((u, packed))
packed = ''
u = 0
continue
if c == ']':
num, prev_string = lstr.pop()
packed = prev_string + packed * num
continue
if c.isdigit():
u = u* 10 + int(c)
continue
packed = packed + c
return packed
Tiếp:
# Method Name: define_true
# Filename: chall.py
# Argument count: 1
# Keyword-only arguments: 0
# Number of locals: 3
# Stack size: 5
# Flags: 0x00000043 (NOFREE | NEWLOCALS | OPTIMIZED)
# First Line: 19
20: 0 LOAD_CONST ('')
2 STORE_FAST (res)
21: 4 SETUP_LOOP (to 42)
6 LOAD_FAST (p)
8 GET_ITER
>> 10 FOR_ITER (to 40)
12 STORE_FAST (packed)
22: 14 LOAD_FAST (res)
16 LOAD_GLOBAL (str)
18 LOAD_GLOBAL (len)
20 LOAD_FAST (packed)
22 CALL_FUNCTION (1 positional argument)
24 CALL_FUNCTION (1 positional argument)
26 LOAD_CONST ('[:]')
28 BINARY_ADD
30 LOAD_FAST (packed)
32 BINARY_ADD
34 INPLACE_ADD
36 STORE_FAST (res)
38 JUMP_ABSOLUTE (to 10)
>> 40 POP_BLOCK
23: >> 42 LOAD_FAST (res)
44 RETURN_VALUE
1 Lưu ý là ta đọc từ dưới lên đối với 1 câu lệnh gán (và tất nhiên là từ phải qua trái)
def define_true(p):
res = ''
for packed in p:
res += str(len(packed)) + '[:]' + packed
return res
và hàm cuối cùng trước khi chạy xem sao:
# Method Name: define_both
# Filename: chall.py
# Argument count: 1
# Keyword-only arguments: 0
# Number of locals: 6
# Stack size: 5
# Flags: 0x00000043 (NOFREE | NEWLOCALS | OPTIMIZED)
# First Line: 25
26: 0 BUILD_LIST 0
2 STORE_FAST (unpacked)
27: 4 SETUP_LOOP (to 86)
6 LOAD_FAST (p)
8 GET_ITER
>> 10 FOR_ITER (to 84)
12 STORE_FAST (i)
28: 14 LOAD_FAST (i)
16 LOAD_ATTR (split)
18 LOAD_CONST (')')
20 CALL_FUNCTION (1 positional argument)
22 STORE_FAST (packed)
29: 24 LOAD_CONST ('')
26 STORE_FAST (char)
30: 28 SETUP_LOOP (to 72)
30 LOAD_FAST (packed)
32 GET_ITER
>> 34 FOR_ITER (to 70)
36 STORE_FAST (j)
31: 38 LOAD_FAST (j)
40 LOAD_CONST ('')
42 COMPARE_OP (==)
44 POP_JUMP_IF_FALSE (to 48)
32: 46 BREAK_LOOP
33: >> 48 LOAD_FAST (j)
50 LOAD_CONST (')')
52 INPLACE_ADD
54 STORE_FAST (j)
35: 56 LOAD_FAST (char)
58 LOAD_GLOBAL (eval)
60 LOAD_FAST (j)
62 CALL_FUNCTION (1 positional argument)
64 INPLACE_ADD
66 STORE_FAST (char)
68 JUMP_ABSOLUTE (to 34)
>> 70 POP_BLOCK
36: >> 72 LOAD_FAST (unpacked)
74 LOAD_ATTR (append)
76 LOAD_FAST (char)
78 CALL_FUNCTION (1 positional argument)
80 POP_TOP
82 JUMP_ABSOLUTE (to 10)
>> 84 POP_BLOCK
37: >> 86 LOAD_FAST (unpacked)
88 RETURN_VALUE
BREAK_LOOP
- câu lệnh break
def define_both(p):
unpacked = []
for i in p:
packed = i.split(')')
char = ''
for j in packed:
if j == '':
break
j += ')'
char += eval(j)
unpacked.append(char)
return unpacked
Và tổng hợp vào và chạy ta được:
def define_both(p):
unpacked = []
for i in p:
packed = i.split(')')
char = ''
for j in packed:
if j == '':
break
j += ')'
char += eval(j)
unpacked.append(char)
return unpacked
def define_true(p):
res = ''
for packed in p:
res += str(len(packed)) + '[:]' + packed
return res
def define_false(s):
lstr = []
u = 0
packed = ''
for c in s:
if c == '[':
lstr.append((u, packed))
packed = ''
u = 0
continue
if c == ']':
num, prev_string = lstr.pop()
packed = prev_string + packed * num
continue
if c.isdigit():
u = u* 10 + int(c)
continue
packed = packed + c
return packed
if __name__ == '__main__':
s = [
'chr(103)chr(48)chr(79)chr(97)chr(116)chr(125)',
'chr(105)chr(115)',
'chr(109)chr(69)chr(51)chr(115)chr(115)chr(105)',
'chr(115)chr(105)chr(85)chr(85)chr(85)',
'chr(75)chr(67)chr(84)chr(70)chr(123)'
]# list gồm 5 phần tử
s = s[::-1]
print(define_false(define_true(define_both(s))))
#KCTF{:::::siUUU::::::mEssi::::::::::::::::::::::::::::::::is::::::gOat}
6: Stegorev
Trong nhiệm vụ lần này thì cho ta một bức ảnh thôi, với tiêu đề Stegorev
thì chắc chắn bài này ta cần tìm thông tin hay file bị giấu ở ảnh này để từ đó reverse nó ra.
Khi ta tìm kiếm các thủ thuật, Hacktricks về stego, thì đã tìm được stegcracker
dùng để bung file đó ra xem có gì không. Sau một thời gian chờ đợi password thì cuối cùng cũng ra (thư viện password mặc định) một file thực thi trên linux.
Với cả xem tham khảo thêm thì ta nhận được thêm 1 tool nữa là Stegseek
.
Quả thật nhanh chưa đầy 3s ấy vậy mà trước mình phải chờ hẳn 30 phút🥲. (link tham khảo writeup này nè …)
Ta nhận được file kctf-rng70.jpg.out
tiếp theo đến phần reverse. Kiểm tra bằng ida pro thì phát hiện là đầu tiên kiểm tra xem có tồn tại file với tên ckrIupRS782prsdsf
:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rax
char v5[32]; // [rsp+0h] [rbp-A0h] BYREF
char v6[47]; // [rsp+20h] [rbp-80h] BYREF
char v7; // [rsp+4Fh] [rbp-51h] BYREF
char v8[32]; // [rsp+50h] [rbp-50h] BYREF
char v9[40]; // [rsp+70h] [rbp-30h] BYREF
std::allocator<char>::allocator(&v7, a2, a3);
std::string::basic_string(v6, "ckrIupRS782prsdsf", &v7); //cái này để truyền địa chỉ cho v6 trỏ tới chuỗi ckrIupRS782prsdsf
sub_2486(v5, v6); // cần check
std::string::~string(v6);
std::allocator<char>::~allocator(&v7);
if ( (unsigned __int8)sub_2C13(v5, "The file maybe lost in the matrix. Try hard to find it") ) //hàm này để so sánh nếu bằng với chuỗi này thì exit
exit(0);
std::string::basic_string(v9, v5);
sub_266F(v8, v9);
v3 = std::operator<<<char>(&std::cout, v8);
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
std::string::~string(v8);
std::string::~string(v9);
std::string::~string(v5);
return 0LL;
}
Chúng ta cùng đi vào hàm sub_2486()
kiểm tra xem trong làm gì:
__int64 __fastcall sub_2486(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
__int64 v3; // rax
__int64 v4; // rax
char v6[32]; // [rsp+10h] [rbp-240h] BYREF
char v7[527]; // [rsp+30h] [rbp-220h] BYREF
char v8[9]; // [rsp+23Fh] [rbp-11h] BYREF
std::ifstream::basic_ifstream(v7);
std::ifstream::open(v7, a2, 8LL);
if ( (unsigned __int8)std::ifstream::is_open(v7) ) // mở file có tên v7 là v2 mà mình đã bàn lúc nãy
{
std::string::basic_string(v6);
std::operator>><char>(v7, v6);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "Flag: ");
v4 = std::operator<<<char>(v3, v6);
}
else
{
std::allocator<char>::allocator(v8, a2, v2);
std::string::basic_string(v6, "The file maybe lost in the matrix. Try hard to find it", v8);
std::allocator<char>::~allocator(v8);
v4 = std::operator<<<char>(&std::cout, v6);
}
std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
std::string::basic_string(a1, v6);
std::string::~string(v6);
std::ifstream::~ifstream(v7);
return a1;
}
Hàm này kiểm tra xem có file tên ckrIupRS782prsdsf
có tồn tại không? nếu có in ra flag còn nếu không thì sẽ in và gán cho nó chuỗi rồi return chuỗi The file maybe lost in the matrix. Try hard to find it
.
Như vậy nếu thỏa mãn thì ta sẽ nhận được như sau:
Như vậy là đã thỏa mãn việc tồn tại file nhưng vẫn không ra đúng. Kiểm tra lại ta thấy là họ lấy giá trị có trong file mình vừa tạo mà lúc nãy mình tạo file rỗng.
Sau khi debug lại ta có: ta truyền dữ liệu bất kì cho file đó rồi chạy thì hiện ra flag kiểu so sánh ?==?
với JBVE~k_lRciOX*=(HG=,0KKEl
mà không biết cái này là gì😂.
Thế là sửa file đó ném vào file đó cái đó thì ta nhận được flag hí hí….
Như vậy là xong bài được nhiều điểm nhất mà tôi cũng tiếc nhất vì làm được đến coi như là 90% hơn rồi.
Cảm ơn mọi người đã đọc đến đây. Hẹn mọi người tại các bài viết khác nhé… Chúc mọi người thành công.🏆