Use AI to summarize documents and generate flowcharts in minutes

How to use AI to summarize documents and generate flowcharts in minutes ?

Transform an online GitBook document into Markdown using a Python script

Jump to github url https://github.com/A2-Security/GitBook-Scraper
Clone repo using the following command

1
git@github.com:A2-Security/GitBook-Scraper.git

Ensure that you have the following installed

  • Python 3.x
  • Python packages: requests, beautifulsoup4, html2text

Let’s take zerolend project as example, the official document url is https://docs.zerolend.xyz.

1
2
3
4
5
if __name__ == '__main__':
gitbook_url = 'https://docs.zerolend.xyz' # Replace with the GitBook URL
output_file = 'documentation.md' # Desired output markdown file

main(gitbook_url, output_file)

Replace above official document url to whatever you need.After modify it to your own url, run the following python script.

1
python3 gitbook_scraper.py

After successlly execute the python script you’ll see the documentation.md on your root directory.

Feed the markdown file to AI

Let’s take claude.ai as example.
claude.ai

Copy the generated flowchart to online mermaid website.

view the flowchart online

Jump to website https://mermaid.live/ , paste the copyed flowchart code to it.

Why we need this ?

As we know, Web3 security competitions are time-limited. Participants who grasp the entire system quickly gain an advantage in identifying more vulnerabilities. With the help of AI we can generate flowchart in minitues. Furthermore, we have a summary of the entire project. I’m not entirely convinced that AI will replace auditors in the near future, but I am certain that those who don’t utilize AI will be left behind.

Audit Daily Approved Tokens To A Pool Can Be Forced To Take

[H] Audit Daily Approved Tokens To A Pool Can Be Forced To Take

Code snippet

full code link

https://github.com

1
2
3
4
5
6
7
8
9
10
function take(
address borrowerAddress_,
uint256 collateral_,
address callee_,
bytes calldata data_
) external override nonReentrant {
...

_transferQuoteTokenFrom(callee_, result.quoteTokenAmount);
}

Impact

Anyone who invokes the toke function and passes in someone else who approved this pool could take the tokens.

Proof of concept

I use foundry to make the whole test.

  • 1.Firstly,we need to create our own ERC20 token and implement the quote contract.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function setUp() public virtual {
console.log("When transferring tokens...");
alice = address(2);
bob = address(3);

myERC20 = new MyERC20();
quote = new Quote(address(myERC20));

//owner mint some token
myERC20.mint(OWNERAMOUNT);

//send some token to our dear testing user.
myERC20.transfer(alice,SOMEAMOUNT);
myERC20.transfer(bob,SOMEAMOUNT);
}
  • 2.Use bob’s address approved the pool some token.Check the balance of bob after we make the call.
1
2
3
4
5
vm.prank(bob);
myERC20.approve(address(quote), SOMEAMOUNT);

//check bob balance step1.
assertEq(myERC20.balanceOf(bob), SOMEAMOUNT);
  • 3.Use Alice’s address to call the take function which is public within the quote contract.
1
2
3
//alice transfer the approve tokens.
vm.prank(alice);
quote.take(address(bob), SOMEAMOUNT);
  • 4.Finally, we check the balance of bob, if bob’s token has been token by anyone who makes the call.
1
2
//check bob balance step2.
assertEq(myERC20.balanceOf(bob), 0);

Alternatively, consider checking that callee has approved spending quote tokens to msg.sender.

1
2
3
4
5
6
7
8
9
10
function take(
address borrowerAddress_,
uint256 collateral_,
address callee_,
bytes calldata data_
) external override nonReentrant {
...

_transferQuoteTokenFrom(msg.sender, result.quoteTokenAmount);
}

union finance high vulnerable on sherlock audit

union finance high vulnerable on sherlock audit

Full Code Url

1
2
3
stakers[vouch.staker].lockedCoinAge +=
(block.number - _max(lastWithdrawRewards, uint256(vouch.lastUpdated))) *
uint256(vouch.locked);

Summary

Voucher can be counted arbitrary many times in staker’s lockedCoinAge. If a voucher has maximized its trust then its locked is added to the lockedCoinAge each time fully as its lastUpdated is kept intact. This provides a surface to grow lockedCoinAge as big as an attacker wants, increasing it by current_block_difference * vouch.locked on each transaction.

Vulnerability Detail

Code Detail:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
uint256 lastWithdrawRewards = getLastWithdrawRewards[vouch.staker];
stakers[vouch.staker].lockedCoinAge +=
(block.number - _max(lastWithdrawRewards, uint256(vouch.lastUpdated))) *
uint256(vouch.locked);
if (lock) {
// Look up the staker and determine how much unlock stake they
// have available for the borrower to borrow. If there is 0
// then continue to the next voucher in the array
uint96 stakerLocked = stakers[vouch.staker].locked;
uint96 stakerStakedAmount = stakers[vouch.staker].stakedAmount;
uint96 availableStake = stakerStakedAmount - stakerLocked;
uint96 lockAmount = _min(availableStake, vouch.trust - vouch.locked);
if (lockAmount == 0) continue;
// Calculate the amount to add to the lock then
// add the extra amount to lock to the stakers locked amount
// and also update the vouches locked amount and lastUpdated block
innerAmount = _min(remaining, lockAmount);
stakers[vouch.staker].locked = stakerLocked + innerAmount;
vouch.locked += innerAmount;
vouch.lastUpdated = uint64(block.number);
} else {
// Look up how much this vouch has locked. If it is 0 then
// continue to the next voucher. Then calculate the amount to
// unlock which is the min of the vouches lock and what is
// remaining to unlock
uint96 locked = vouch.locked;
if (locked == 0) continue;
innerAmount = _min(locked, remaining);
// Update the stored locked values and last updated block
stakers[vouch.staker].locked -= innerAmount;
vouch.locked -= innerAmount;
vouch.lastUpdated = uint64(block.number);
}

Above code is invoked where user called borrow() function with amount>minBorrow:
code

1
2
3
4
5
6
7
8
9
10
11
function borrow(address to, uint256 amount) external override onlyMember(msg.sender) whenNotPaused nonReentrant {
IAssetManager assetManagerContract = IAssetManager(assetManager);
if (amount < minBorrow) revert AmountLessMinBorrow();
if (amount > getRemainingDebtCeiling()) revert AmountExceedGlobalMax();

...

// Call update locked on the userManager to lock this borrowers stakers. This function
// will revert if the account does not have enough vouchers to cover the borrow amount. ie
// the borrower is trying to borrow more than is able to be underwritten
IUserManager(userManager).updateLocked(msg.sender, (actualAmount + fee).toUint96(), true);
1
uint96 lockAmount = _min(availableStake, vouch.trust - vouch.locked);

When vouch.trust == vouch.locked the value of lockAmount goes to zero. And the loop continued.Thus the value of lastUpdated doesn’t updated.

Suppose Bob the staker has a vouch with trust maxxed, i.e. vouch.trust = vouch.locked = 10k DAI. He can setup a second borrower being his own account, some minimal trust, then can run min borrow many, many times, gaining huge stakers[vouch.staker].lockedCoinAge as vouch.lastUpdated aren’t updated and lockedCoinAge grows with a positive some_number_of_blocks * 10k DAI number each time Bob borrows 1 DAI via his second borrower.

Recommendation

1
2
3
4
5
6
7
8
9
10
11
12
13
            uint256 lastWithdrawRewards = getLastWithdrawRewards[vouch.staker];
stakers[vouch.staker].lockedCoinAge +=
(block.number - _max(lastWithdrawRewards, uint256(vouch.lastUpdated))) *
uint256(vouch.locked);
+ vouch.lastUpdated = uint64(block.number);
if (lock) {
...
- vouch.lastUpdated = uint64(block.number);
} else {
...
- vouch.lastUpdated = uint64(block.number);
}