PINsmith
Author: 0xIDA
Platform: HTB · Category: coding · Difficulty: easy
Solvers
Summary
A coding challenge where we must generate all possible PIN combinations matching a known partial pattern, respecting a "no adjacent repeats" policy. The pattern contains known digits (0-9) and wildcards (*). The solution uses backtracking to enumerate all valid PINs in lexicographical order.
Solution
Step 1: Understand the problem
The challenge presents a scenario where a CygnusCorp system is locked behind a numeric PIN. Some digits of the PIN are known, others are unknown (*). The security policy states that no two adjacent digits can be the same. The PIN length is between 2 and 10 digits.
Input format: A single string with known digits and * for unknown positions.
Output format: All valid PINs, one per line, sorted ascending lexicographically.
Step 2: Solve with backtracking
The approach uses depth-first search with backtracking:
For each position in the pattern:
- If the digit is known (not *): place it only if it differs from the previous digit
- If the digit is unknown (*): try each digit 0-9 that differs from the previous digit
- When all positions are filled, add the PIN to results
The lexicographic order is automatically satisfied by trying digits from '0' to '9'.
Step 3: Submit the solution
The challenge runs on a coding platform with a Monaco editor and a /run API endpoint. Submitting the Python solution passes all hidden test cases and returns the flag.
Python solution:
import sys
def solve():
pattern = sys.stdin.readline().strip()
n = len(pattern)
res = []
def backtrack(pos, cur):
if pos == n:
res.append(''.join(cur))
return
if pattern[pos] != '*':
d = pattern[pos]
if not cur or cur[-1] != d:
cur.append(d)
backtrack(pos + 1, cur)
cur.pop()
else:
for d in '0123456789':
if not cur or cur[-1] != d:
cur.append(d)
backtrack(pos + 1, cur)
cur.pop()
backtrack(0, [])
sys.stdout.write('\n'.join(res))
if __name__ == '__main__':
solve()
Verification example:
| Pattern | Valid PINs Count | Samples |
|---|---|---|
1*3 | 8 | 103, 123, 143, 153, 163, 173, 183, 193 |
0*0 | 9 | 010, 020, 030, 040, 050, 060, 070, 080, 090 |
**** | 7,290 | 0101, 0102, ..., 9897 |
12** | 81 | 1201, 1202, ..., 1298 |
Step 4: Receive the flag
After submitting the code via /run API, all test cases pass and the flag is returned.
Flag
HTB{3ff1c13nt_P1n_Cr4cK1nG}
Remediation (real-world takeaway)
- No adjacent repeats is an extremely weak constraint — for a 4-digit PIN with 2 wildcards, there are still 81 possible values. For a fully unknown 4-digit PIN, 7,290 values remain.
- PIN-based authentication should use rate limiting, account lockout, and MFA to prevent brute-force attacks.
- Leaking even partial PIN digits dramatically reduces the search space — store only hashed credentials.
- For systems where educated brute-forcing is a concern, enforce cryptographically random secrets with sufficient entropy.