Files
CTF/DownUnderCTF 2023/blockchain/ Another Please/README.md
2023-09-02 14:08:40 +02:00

111 lines
3.5 KiB
Markdown

# Another Please
```
A smart contract system is selling DownUnderLand tickets! Can you get them all?
Goal: Own all 30 tickets to DownUnderLand in your player wallet.
Author: BlueAlder
Estimated startup time: 90 seconds
```
# setup
Siehe "Eight Five Four Five"
# Source
```sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
/**
* ______ _ _ _ _ _
* | _ \ | | | | | | | | | |
* | | | |_____ ___ __ | | | |_ __ __| | ___ _ __| | __ _ _ __ __| |
* | | | / _ \ \ /\ / / '_ \| | | | '_ \ / _` |/ _ \ '__| | / _` | '_ \ / _` |
* | |/ / (_) \ V V /| | | | |_| | | | | (_| | __/ | | |___| (_| | | | | (_| |
* |___/ \___/ \_/\_/ |_| |_|\___/|_| |_|\__,_|\___|_| \_____/\__,_|_| |_|\__,_|
*/
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract AnotherPlease is ERC721Enumerable {
uint256 public constant TICKETS_TO_GIVE_AWAY = 10;
uint256 public constant PURCHASABLE_TICKETS = 20;
uint256 public constant TICKET_PRICE = 10000000000000 ether;
mapping(address => bool) public freeTicketReceivers;
uint256 public ticketsGivenAway;
error FreeTicketAlreadyClaimed();
error FreeTicketsExhausted();
error NotEnoughFunds();
error SoldOut();
constructor() ERC721("DownUnderLand Tickets", "DUCTF_ENTRY") {}
modifier ticketNotClaimed() {
if (freeTicketReceivers[msg.sender]) revert FreeTicketAlreadyClaimed();
_;
}
// The first 20 people to claim a ticket get it freeeeeeeeeeeeeeeee!
function claimFreeTicket() external ticketNotClaimed {
if (ticketsGivenAway >= TICKETS_TO_GIVE_AWAY) revert FreeTicketsExhausted();
_gibTicket(msg.sender);
ticketsGivenAway++;
freeTicketReceivers[msg.sender] = true;
}
// Buy a ticket to the exclusive DownUnderLand Party!!!!!
// Cheap price!
function buyATicket() external payable {
if (msg.value < TICKET_PRICE) revert NotEnoughFunds();
_gibTicket(msg.sender);
uint256 change = TICKET_PRICE - msg.value;
if (change > 0) {
(bool success,) = msg.sender.call{value: change}("");
require(success, "Bruh do u not want ur money back?");
}
}
function totalTicketsAvailable() public pure returns (uint256) {
return TICKETS_TO_GIVE_AWAY + PURCHASABLE_TICKETS;
}
function _gibTicket(address to) internal {
if (totalSupply() >= totalTicketsAvailable()) revert SoldOut();
_safeMint(to, totalSupply());
}
}
```
# Analyse
Ziel ist es alle 30 Tickets zu besitzen. Glücklicherweise kriegen wir die ersten 10 gratis, aber die letzten 20 sind teurer als wir es uns leisten können.
Die Methode ```claimFreeTicket``` prüft erst, ob noch gratis Tickets vorhanden sind, und sendet dann das Ticket und decrementiert dann den Counter. Und hier liegt die Schwachstelle:
Der Angreifer Callt schnell hintereinander die ```claimFreeTicket``` Methode. Das Übertragen der Tickets via ```_safeMint``` triggert vermutlich einen call auf uns, was uns erlaubt die methode erneut aufzurufen. Wenn der Counter heruntergesetzt wird, werden wir schon alle Tickets besitzen.
```
_safeMint(address to, uint256 tokenId)
internal
Safely mints tokenId and transfers it to to.
Requirements:
tokenId must not exist.
If to refers to a smart contract, it must implement IERC721Receiver.onERC721Received, which is called upon a safe transfer.
Emits a Transfer event.
```
# Lösung
#TODO