This post has writeups for the n1ctf 2021.  
## 1. Signin (web)  
1\. In this challenge, we are given a link to a PHP website. The source code
is as follows:  
```php  
`logpath:$name`";  
}  
$check=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($path));  
if(is_file($check)){  
echo "

    
    
    ".file_get_contents($check)."

";  
}  
```  
2\. We have a hint saying that the flag is at the path "/flag". We have a
'path' variable that takes input from the POST request and we have a 'time'
variable that takes input from the raw POST data only if the time variable in
GET request is set. So we control the inputs in two places.  
3\. There is a blacklist for the POST variable 'path'. It means that we cannot
set 'path' to be '/flag'. Let's look at the other input variable 'time'. It
takes as input the raw POST data of request and saves it in a file. That
filename is returned back to the user.  
4\. If the 'path' variable is not empty, it is passed to a blacklist filter,
the contents of the file specified by 'path' are put through a regex, and
finally this filtered data is assumed to be a file path. We read that file and
send the contents to the user.  
5\. So the solution is that we send '\/\f\l\a\g' as raw POST data to the
'time' variable. The backslashes stop the date function from parsing the input
as a date. It will be saved as a file and that file location will be returned
to us. We take that file location and pass it the 'path' variable in the POST
request. The contents of '/flag' are returned to us.  
6\. Flag: n1ctf{bypass_date_1s_s000_eassssy}  

Original writeup
(https://github.com/gum3ng/ctf_writeups/blob/main/n1ctf/n1ctf.md#1-signin-
web).Writeup for new web ctfer

Original writeup (https://www.gem-love.com/ctf/2657.html#websignin).程序是python写的，这一点可以从IDA打开后<kbd>shift</kbd>+<kbd>f12</kbd>打开Strings
Window看出来，各种`py`开头的字符串遍地走。细心一点可以确认这是pyinstaller打包而不是py2exe。

先使用[pyinstxtractor](https://github.com/extremecoders-
re/pyinstxtractor)解开，还原Python代码。命令：`python pyinstxtractor.py signin.exe`

目录`\signin.exe_extracted`下，使用uncompyle6获得`main.pyc`源码。（值得注意的是，pyinstxtractor解开的时候有显示是**python38**！如果在python37环境下使用uncompyle6则可能不能得到完整的代码。

根据main.py自行反编译需要的pyc文件。可以看出程序是PyQt5写得，**程序启动时释放了一个DLL**，退出时删除。所以可以运行时把tmp.dll复制出去分析。

可以看到（看下面代码注释）：  
```python  
# main.py  
# ....  
class AccountChecker:

def __init__(self):  
self.dllname = './tmp.dll'  
self.dll = self._AccountChecker__release_dll()  
self.enc = self.dll.enc  
  
# 这里已经表明调用dll中的 enc 函数，  
# 函数名参数类型和返回值都体现出来了，分析dll就方便很多了  
self.enc.argtypes = (c_char_p, c_char_p, c_char_p, c_int)  
self.enc.restype = c_int

self.accounts = {b'SCTFer':
b64decode(b'PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA')}  
self.try_times = 0  
# ....  
```

整体逻辑就是调用dll对用户名和密码进行加密，返回python部分进行校验。dll里面enc的逻辑比较简单，参数分别是  
```  
username, password, safe_password_recv, buffersize  
```  
有需要的话可以使用类似下面的代码进行对dll的调试  
```c  
#include <stdio.h>  
#include <Windows.h>

int main() {  
HMODULE module = LoadLibraryA(".\\\enc.dll");  
if (module == NULL) {  
printf("failed to load");  
return 1;  
}

typedef int(*EncFun)(char*, char*, char*, int);  
EncFun enc;  
enc = (EncFun)GetProcAddress(module, "enc");

char username[20] = "SCTFer" };  
char pwd[33] = { "SCTF{test_flag}" };  
char recv[33] = { 0 };  
printf("%d",enc(username, pwd, recv, 32));  
return 0;  
}  
```

加密逻辑不是很复杂，这里不再赘述，注意字节序就好，解题脚本如下：  
```python  
from base64 import *  
import struct

def u_qword(a):  
return struct.unpack('<Q', a)[0]

def p_qword(a):  
return struct.pack('<Q', a)

username = list(b'SCTFer')  
enc_pwd =
list(b64decode(b'PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA'))[:-1] # remove
the last '\0'  
for i in range(32):  
enc_pwd[i] ^= username[i % len(username)]

qwords = []  
for i in range(4):  
qwords.append(u_qword(bytes(enc_pwd[i*8: i*8 + 8])))

for i in range(4):  
qword = qwords[i]  
for _ in range(64):  
if qword & 1 == 1:  
# 如果最低位是1，说明加密时左移后，  
# 和12682219522899977907进行了异或  
qword ^= 12682219522899977907  
qword >>= 1  
qword |= 0x8000000000000000  
continue  
else:  
qword >>= 1  
# print(qword)  
qwords[i] = qword

pwd = []  
for i in range(4):  
pwd.append(p_qword(qwords[i]).decode())

flag = ''.join(pwd)  
print(flag)  
```  
得到flag `SCTF{We1c0m3_To_Sctf_2020_re_!!}`