w4nk3r
发布于 IP属地辽宁省

对一vpn软件的逻辑漏洞挖掘(薅羊毛)

1111111

这里先大概讲一下 薅羊毛漏洞 的逻辑
一般都是这四大金刚导致的
1.用户标识机制不完善
2.优惠券发放的逻辑存在缺陷
3.相关的业务逻辑关键点设计的不严谨
4.开发的代码写错了QAQ

挨个说明一下

用户标识机制不完善

依赖单一标识符: 系统仅以邮箱、手机号等单一信息作为用户唯一标识,未建立综合身份识别机制(如设备指纹、实名信息等)。漏洞风险就是用户注销后,使用同一信息重新注册即可被系统视为新用户。这种情况在很多企业中并不算很少见,下面为我跟某src审核聊天得知的这一情况

未保留注销记录:系统在用户注销后彻底删除其数据,或未将注销账户信息与优惠券领取记录关联,导致无法识别历史用户。本次介绍的漏洞就是这个情况,利用的薄弱点,测试用户可能不关联记录,从而发掘出该漏洞

下面代码就是存在该漏洞的直观体现

白盒展示

@Service
public class VulnerableUserService {

@Autowired
private UserRepository userRepository;

// 注册逻辑:仅检查邮箱是否存在
public User register(String email, String password) {
    // 漏洞点:仅通过邮箱判断用户是否存在,未考虑已注销账户
    if (userRepository.findByEmail(email) != null) {
        throw new RuntimeException("邮箱已注册");
    }
    User newUser = new User(email, password);
    return userRepository.save(newUser);
}

// 注销逻辑:直接删除所有数据
public void deleteAccount(Long userId) {
    userRepository.deleteById(userId); 
}

}

优惠券发放逻辑存在缺陷

新用户判定条件简单:优惠券发放仅以账户是否“首次注册”为判断条件,未考虑用户注销后重新注册的情况,导致漏洞被利用。

未关联历史行为:发放优惠券时,系统未查询该用户身份(包括已注销账户)的历史领取记录,导致重复发放。

白盒展示

优惠券服务
@Service
public class VulnerableCouponService {

@Autowired
private UserRepository userRepository;

// 发放优惠券逻辑
public Coupon grantCoupon(String email) {
    User user = userRepository.findByEmail(email);
    
    // 漏洞点:只检查当前是否存在账户,不检查历史注销账户(
    if (user == null) {
        throw new RuntimeException("用户不存在");
    }

    // 漏洞点:仅检查该账户是否领取过,不关联历史账户
    if (user.getCoupons().isEmpty()) { 
        Coupon coupon = new Coupon("NEW_USER_100"); 
        user.getCoupons().add(coupon);
        userRepository.save(user);
        return coupon;
    } else {
        throw new RuntimeException("已领取过优惠券");
    }
}

}

业务流程设计疏忽

注销机制与优惠券逻辑未联动: 产品设计时未考虑用户注销场景对营销活动的影响,两个功能模块独立运作,缺乏数据互通。

未设置冷却机制:同一身份信息频繁注册/注销时,缺乏风险控制策略(如限制同一设备/IP的注册频率),这也就是常说的并发

白盒展示

// 注册控制器 @RestController public class VulnerableAuthController {
@Autowired
private VulnerableUserService userService;

@PostMapping("/register")
public User register(@RequestParam String email, 
                    @RequestParam String password) {
    // 漏洞点:无设备指纹、无IP频率限制
    return userService.register(email, password);
}

}

技术实现漏洞

数据库设计缺陷: 用户表与优惠券领取记录表未正确关联,或注销操作未触发领取状态重置,两个逻辑各忙各的,导致数据不一致。

代码逻辑错误: 例如,检查用户是否存在时,未包含已注销账户,错误判断为新用户。

白盒展示

用户逻辑

@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String email;

// 漏洞点:无软删除标记,物理删除后无法追溯
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Coupon> coupons = new ArrayList<>();

}

// 用户Repository直接使用JPA默认删除
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email); // 无法查询已删除用户
}

ok,废话不多说,上实战

打开该软件界面,上来就是测试用户,免费送两天

我们现在是临时账号,可以猜测一下我们的数据没有存储到数据库中,可以达到每次重置都以新用户的身份登录,为了验证一下,特地等到仅剩一天

OK,清除一手缓存,验证猜想

重新注册,发现我们的时间又变为两天了

浏览 (91)
点赞 (1)
收藏
打赏
评论